init
This commit is contained in:
291
raw/maps/mp/_adrenaline.gsc
Normal file
291
raw/maps/mp/_adrenaline.gsc
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
Exo Overclock
|
||||
Author: Keith Evans
|
||||
Description: A piece of equipment used to give you temporarily increased speed
|
||||
*/
|
||||
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\perks\_perkfunctions;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
CONST_Adrenaline_SpeedScale = 1.12;
|
||||
CONST_Adrenaline_Timeout = 10;
|
||||
|
||||
watchAdrenalineUsage() // self = player
|
||||
{
|
||||
self notify( "exo_overclock_taken" ); // Cleans up existing Exo Overclock threads.
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_overclock_taken" );
|
||||
|
||||
if ( !self HasWeapon( "adrenaline_mp" ) )
|
||||
return;
|
||||
|
||||
self AdrenalineInit();
|
||||
|
||||
self thread MonitorPlayerDeath();
|
||||
self thread wait_for_game_end();
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "exo_adrenaline_fire" );
|
||||
|
||||
if( !IsAlive( self ) )
|
||||
return;
|
||||
|
||||
self thread tryUseAdrenaline();
|
||||
}
|
||||
}
|
||||
|
||||
AdrenalineInit()
|
||||
{
|
||||
self.overclock_on = false;
|
||||
|
||||
self BatterySetDischargeScale( "adrenaline_mp", 1.0 );
|
||||
full_energy = self BatteryGetSize( "adrenaline_mp" );
|
||||
|
||||
if ( self GetTacticalWeapon() == "adrenaline_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", full_energy );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == "adrenaline_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", full_energy );
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.exo_overclock_vfx_le_active ) )
|
||||
level.exo_overclock_vfx_le_active = LoadFX( "vfx/lights/exo_overclock_hip_le_start" );
|
||||
if ( !IsDefined( level.exo_overclock_vfx_ri_active ) )
|
||||
level.exo_overclock_vfx_ri_active = LoadFX( "vfx/lights/exo_overclock_hip_ri_start" );
|
||||
if ( !IsDefined( level.exo_overclock_vfx_le_inactive ) )
|
||||
level.exo_overclock_vfx_le_inactive = LoadFX( "vfx/lights/exo_overclock_hip_le_inactive" );
|
||||
if ( !IsDefined( level.exo_overclock_vfx_ri_inactive ) )
|
||||
level.exo_overclock_vfx_ri_inactive = LoadFX( "vfx/lights/exo_overclock_hip_ri_inactive" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !inVirtualLobby() )
|
||||
{
|
||||
//turn on fx for inactive exo overclock
|
||||
PlayFXOnTag( level.exo_overclock_vfx_le_inactive, self, "J_Hip_LE" );
|
||||
PlayFXOnTag( level.exo_overclock_vfx_ri_inactive, self, "J_Hip_RI" );
|
||||
}
|
||||
}
|
||||
|
||||
tryUseAdrenaline( ) // self == player
|
||||
{
|
||||
self endon( "exo_overclock_taken" );
|
||||
|
||||
if ( self.overclock_on == true )
|
||||
{
|
||||
self thread StopAdrenaline( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread StartAdrenaline();
|
||||
}
|
||||
}
|
||||
|
||||
killOverclockFx() // self == player
|
||||
{
|
||||
if ( IsDefined( self.overclock_fx_l ) )
|
||||
{
|
||||
self.overclock_fx_l delete();
|
||||
self.overclock_fx_l = undefined;
|
||||
}
|
||||
if ( IsDefined( self.overclock_fx_r ) )
|
||||
{
|
||||
self.overclock_fx_r delete();
|
||||
self.overclock_fx_r = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
StartAdrenaline() //self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_overclock_taken" );
|
||||
self endon( "EndAdrenaline" );
|
||||
|
||||
self.overclock_on = true;
|
||||
|
||||
// Need to handle this here because Exo Overclock is not processed like a grenade as other Exo Abilities are.
|
||||
maps\mp\gametypes\_gamelogic::setHasDoneCombat( self, true );
|
||||
|
||||
self.adrenaline_speed_scalar = CONST_Adrenaline_SpeedScale;
|
||||
|
||||
if ( self _hasPerk( "specialty_lightweight" ) )
|
||||
{
|
||||
// lightWeightScalar() does not return the percent speed boost from lightweight but the full scalar, hence the -1.
|
||||
self.moveSpeedScaler = self.adrenaline_speed_scalar + ( lightWeightScalar() - 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.moveSpeedScaler = self.adrenaline_speed_scalar;
|
||||
}
|
||||
|
||||
//for coop - set movespeed to be based of the class setting
|
||||
if ( isdefined ( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
class = self GetClientOmnvar ( "ui_horde_player_class" );
|
||||
self.moveSpeedScaler = min ( ( level.classSettings[class]["speed"] + 0.25 ), CONST_Adrenaline_SpeedScale );
|
||||
}
|
||||
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
|
||||
self BatteryDischargeBegin( "adrenaline_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "adrenaline_mp", "ui_exo_battery_toggle", 1 );
|
||||
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( "adrenaline_mp" );
|
||||
|
||||
self thread monitor_overclock_battery_charge();
|
||||
|
||||
// activation sound
|
||||
self snd_message( "mp_exo_overclock_activate" );
|
||||
|
||||
// vfx
|
||||
self killOverclockFx();
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !self.overclock_on ) //turned off during wait?
|
||||
return;
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.overclock_fx_l = SpawnLinkedFx( level.exo_overclock_vfx_le_active, self, "J_Hip_LE" );
|
||||
self.overclock_fx_r = SpawnLinkedFx( level.exo_overclock_vfx_ri_active, self, "J_Hip_RI" );
|
||||
TriggerFX( self.overclock_fx_l );
|
||||
TriggerFX( self.overclock_fx_r );
|
||||
}
|
||||
|
||||
//jwells - overlay VFX handled in code so killcam will work
|
||||
}
|
||||
|
||||
StopAdrenaline( should_play_fx ) // self = player
|
||||
{
|
||||
if ( !isDefined( self.overclock_on ) || !self.overclock_on )
|
||||
return;
|
||||
|
||||
if ( !IsDefined( should_play_fx ) )
|
||||
{
|
||||
should_play_fx = true;
|
||||
}
|
||||
|
||||
self notify( "EndAdrenaline" );
|
||||
|
||||
self.overclock_on = false;
|
||||
|
||||
if ( self _hasPerk( "specialty_lightweight" ) )
|
||||
{
|
||||
self.moveSpeedScaler = lightWeightScalar();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.moveSpeedScaler = level.basePlayerMoveScale;
|
||||
}
|
||||
|
||||
//for coop - set movespeed back to the class setting
|
||||
if ( isdefined ( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
class = self GetClientOmnvar ( "ui_horde_player_class" );
|
||||
self.moveSpeedScaler = level.classSettings[class]["speed"];
|
||||
}
|
||||
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
|
||||
self.adrenaline_speed_scalar = undefined;
|
||||
|
||||
self BatteryDischargeEnd( "adrenaline_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "adrenaline_mp", "ui_exo_battery_toggle", 0 );
|
||||
|
||||
self killOverclockFx();
|
||||
|
||||
if ( should_play_fx == true )
|
||||
{
|
||||
// deactivation sound
|
||||
self snd_message( "mp_exo_overclock_deactivate" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.overclock_fx_l = SpawnLinkedFx( level.exo_overclock_vfx_le_inactive, self, "J_Hip_LE" );
|
||||
self.overclock_fx_r = SpawnLinkedFx( level.exo_overclock_vfx_ri_inactive, self, "J_Hip_RI" );
|
||||
TriggerFX( self.overclock_fx_l );
|
||||
TriggerFX( self.overclock_fx_r );
|
||||
}
|
||||
}
|
||||
|
||||
//jwells - overlay VFX handled in code so killcam will work
|
||||
}
|
||||
|
||||
MonitorPlayerDeath()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self waittill_any( "death", "joined_team", "faux_spawn", "exo_overclock_taken" );
|
||||
|
||||
self thread StopAdrenaline( false );
|
||||
}
|
||||
|
||||
monitor_overclock_battery_charge() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_overclock_taken" );
|
||||
self endon( "EndAdrenaline" );
|
||||
|
||||
while ( self.overclock_on == true )
|
||||
{
|
||||
if ( self BatteryGetCharge( "adrenaline_mp" ) <= 0 )
|
||||
{
|
||||
thread StopAdrenaline( true );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
take_exo_overclock() //self = player
|
||||
{
|
||||
self notify( "kill_battery" );
|
||||
self notify( "exo_overclock_taken" );
|
||||
self takeWeapon( "adrenaline_mp" );
|
||||
}
|
||||
|
||||
give_exo_overclock() //self = player
|
||||
{
|
||||
self giveWeapon( "adrenaline_mp" );
|
||||
self thread watchAdrenalineUsage();
|
||||
}
|
||||
|
||||
wait_for_game_end() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_overclock_taken" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
self thread StopAdrenaline( false );
|
||||
}
|
596
raw/maps/mp/_aerial_pathnodes.gsc
Normal file
596
raw/maps/mp/_aerial_pathnodes.gsc
Normal file
@ -0,0 +1,596 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
SCR_CONST_AERIAL_PATHNODE_OFFSET_DEFAULT = 500;
|
||||
SCR_CONST_AERIAL_PATHNODE_GROUP_CONNECT_DIST_DEFAULT = 250;
|
||||
SCR_CONST_AERIAL_PATHNODE_MAX_GROUP_CONNECTIONS = 3;
|
||||
|
||||
SCR_CONST_DEBUG_DONT_CONNECT_GROUPS = false;
|
||||
SCR_CONST_DEBUG_SHOW_NODE_GROUPS = false;
|
||||
SCR_CONST_DEBUG_SHOW_ADDED_LINKS = false;
|
||||
|
||||
waittill_aerial_pathnodes_calculated()
|
||||
{
|
||||
while(!IsDefined(level.calculated_aerial_nodes_done) || !level.calculated_aerial_nodes_done)
|
||||
wait(0.5);
|
||||
}
|
||||
|
||||
get_aerial_offset()
|
||||
{
|
||||
if ( IsDefined(level.aerial_pathnode_offset) )
|
||||
return (0,0,level.aerial_pathnode_offset);
|
||||
else
|
||||
return (0,0,SCR_CONST_AERIAL_PATHNODE_OFFSET_DEFAULT);
|
||||
}
|
||||
|
||||
get_group_connect_dist()
|
||||
{
|
||||
if ( IsDefined(level.aerial_pathnode_group_connect_dist) )
|
||||
{
|
||||
if ( level.nextgen )
|
||||
Assert(level.aerial_pathnode_group_connect_dist <= 650);
|
||||
else
|
||||
Assert(level.aerial_pathnode_group_connect_dist <= 500);
|
||||
return level.aerial_pathnode_group_connect_dist;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCR_CONST_AERIAL_PATHNODE_GROUP_CONNECT_DIST_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
node_is_valid__to_convert_to_aerial_pathnode( node )
|
||||
{
|
||||
return (node.type == "Path" && NodeExposedToSky( node, true ) && !node NodeIsDisconnected() );
|
||||
}
|
||||
|
||||
calculate_aerial_pathnodes()
|
||||
{
|
||||
if ( IsDefined(level.calculated_aerial_nodes_in_progress) || IsDefined(level.calculated_aerial_nodes_done) )
|
||||
return;
|
||||
|
||||
mapname = GetDvar( "mapname" );
|
||||
if ( mapname == getdvar( "virtualLobbyMap") || mapname == "mp_character_room" || GetDvarInt( "virtualLobbyActive" ) == 1 )
|
||||
return;
|
||||
|
||||
level.calculated_aerial_nodes_in_progress = true;
|
||||
level.calculated_aerial_nodes_done = false;
|
||||
|
||||
wait(0.5);
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized("ai_showNodesAerial", 0);
|
||||
#/
|
||||
|
||||
// First make a list of all aerial nodes and their aerial neighbors
|
||||
level.aerial_pathnodes = [];
|
||||
all_nodes = GetAllNodes();
|
||||
foreach( node in all_nodes )
|
||||
{
|
||||
if ( node_is_valid__to_convert_to_aerial_pathnode(node) )
|
||||
{
|
||||
level.aerial_pathnodes[level.aerial_pathnodes.size] = node;
|
||||
if ( !IsDefined(node.aerial_neighbors) )
|
||||
node.aerial_neighbors = [];
|
||||
|
||||
neighbors = GetLinkedNodes(node);
|
||||
foreach( neighbor_node in neighbors )
|
||||
{
|
||||
if ( node_is_valid__to_convert_to_aerial_pathnode(neighbor_node) && !array_contains(node.aerial_neighbors, neighbor_node) )
|
||||
{
|
||||
node.aerial_neighbors[node.aerial_neighbors.size] = neighbor_node;
|
||||
|
||||
// To handle the case of one-way links, we make sure to add the link to the neighbor back to this node (in case that node doesn't have it)
|
||||
if ( !IsDefined(neighbor_node.aerial_neighbors) )
|
||||
neighbor_node.aerial_neighbors = [];
|
||||
|
||||
if ( !array_contains(neighbor_node.aerial_neighbors, node) )
|
||||
neighbor_node.aerial_neighbors[neighbor_node.aerial_neighbors.size] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
all_nodes = undefined;
|
||||
wait(0.05);
|
||||
|
||||
// Next divide the nodes into disjoint sets
|
||||
node_groups = divide_nodes_into_groups( level.aerial_pathnodes, 1 );
|
||||
max_group_connections = SCR_CONST_AERIAL_PATHNODE_MAX_GROUP_CONNECTIONS;
|
||||
|
||||
if ( !SCR_CONST_DEBUG_DONT_CONNECT_GROUPS )
|
||||
{
|
||||
// See if we can connect some of these disjoint sets
|
||||
close_dist = get_group_connect_dist();
|
||||
potential_aerial_neighbor_nodes = [];
|
||||
aerial_nodes_processed = 0;
|
||||
|
||||
for( i=0; i<node_groups.size; i++ )
|
||||
{
|
||||
if ( !IsDefined( potential_aerial_neighbor_nodes[i] ) )
|
||||
potential_aerial_neighbor_nodes[i] = [];
|
||||
|
||||
foreach( node in node_groups[i] )
|
||||
{
|
||||
for( j=i+1; j<node_groups.size; j++ )
|
||||
{
|
||||
if ( !IsDefined( potential_aerial_neighbor_nodes[i][j] ) )
|
||||
potential_aerial_neighbor_nodes[i][j] = [];
|
||||
|
||||
potential_neighbors_node_in_i_to_group_j = [];
|
||||
foreach( other_node in node_groups[j] )
|
||||
{
|
||||
dist = Distance(node.origin,other_node.origin);
|
||||
within_close_dist = dist < close_dist;
|
||||
force_connection = false;
|
||||
if ( !within_close_dist )
|
||||
{
|
||||
if ( IsDefined(level.aerial_pathnodes_force_connect) )
|
||||
{
|
||||
foreach( override in level.aerial_pathnodes_force_connect)
|
||||
{
|
||||
radius_sq = squared(override.radius);
|
||||
if ( Distance2DSquared(override.origin,node.origin) < radius_sq && Distance2DSquared(override.origin,other_node.origin) < radius_sq )
|
||||
{
|
||||
force_connection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
room_in_array = potential_neighbors_node_in_i_to_group_j.size < max_group_connections || dist < potential_neighbors_node_in_i_to_group_j[max_group_connections-1][2];
|
||||
if ( within_close_dist && room_in_array )
|
||||
{
|
||||
if ( potential_neighbors_node_in_i_to_group_j.size == max_group_connections )
|
||||
potential_neighbors_node_in_i_to_group_j[max_group_connections-1] = undefined;
|
||||
|
||||
potential_neighbors_node_in_i_to_group_j[potential_neighbors_node_in_i_to_group_j.size] = [node,other_node,dist];
|
||||
potential_neighbors_node_in_i_to_group_j = array_sort_with_func( potential_neighbors_node_in_i_to_group_j, ::is_pair_a_closer_than_pair_b );
|
||||
}
|
||||
else if ( force_connection )
|
||||
{
|
||||
potential_aerial_neighbor_nodes[i][j][potential_aerial_neighbor_nodes[i][j].size] = [node,other_node,-1];
|
||||
}
|
||||
}
|
||||
|
||||
foreach( node_pair in potential_neighbors_node_in_i_to_group_j )
|
||||
potential_aerial_neighbor_nodes[i][j][potential_aerial_neighbor_nodes[i][j].size] = node_pair;
|
||||
}
|
||||
|
||||
aerial_nodes_processed++;
|
||||
if ( aerial_nodes_processed >= 50 )
|
||||
{
|
||||
aerial_nodes_processed = 0;
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now sort the array of potential aerial neighbors, so we can check the closest pairs first
|
||||
wait(0.05);
|
||||
num_sorted = 0;
|
||||
for( i=0; i<node_groups.size; i++ )
|
||||
{
|
||||
for( j=i+1; j<node_groups.size; j++ )
|
||||
{
|
||||
num_sorted += potential_aerial_neighbor_nodes[i][j].size;
|
||||
potential_aerial_neighbor_nodes[i][j] = array_sort_with_func( potential_aerial_neighbor_nodes[i][j], ::is_pair_a_closer_than_pair_b, 150 );
|
||||
|
||||
if ( num_sorted > 500 )
|
||||
{
|
||||
wait(0.05);
|
||||
num_sorted = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
wait(0.05);
|
||||
|
||||
// Now iterate through potential_aerial_neighbor_nodes doing traces. If they succeed, the nodes are added to each others' neighbor lists
|
||||
aerial_offset = get_aerial_offset();
|
||||
increment = 10;
|
||||
num_traces = 0;
|
||||
if ( SCR_CONST_DEBUG_SHOW_ADDED_LINKS )
|
||||
level.added_aerial_links = [];
|
||||
|
||||
for( i=0; i<node_groups.size; i++ )
|
||||
{
|
||||
for( j=i+1; j<node_groups.size; j++ )
|
||||
{
|
||||
foreach ( pair in potential_aerial_neighbor_nodes[i][j] )
|
||||
{
|
||||
node0 = pair[0];
|
||||
node1 = pair[1];
|
||||
|
||||
if ( !node0_has_neighbor_connected_to_node1(node0, node1) )
|
||||
{
|
||||
connections_0_to_group_1 = num_node_connections_to_group(node0,node1.aerial_group);
|
||||
connections_1_to_group_0 = num_node_connections_to_group(node1,node0.aerial_group);
|
||||
if ( connections_0_to_group_1 < max_group_connections && connections_1_to_group_0 < max_group_connections )
|
||||
{
|
||||
hitPos = PlayerPhysicsTrace( node0.origin + aerial_offset, node1.origin + aerial_offset );
|
||||
num_traces++;
|
||||
trace_succeeded = DistanceSquared( hitPos, node1.origin + aerial_offset ) < 1;
|
||||
if ( !trace_succeeded && pair[2] == -1 )
|
||||
{
|
||||
// This is a force pair, so try another trace
|
||||
trace_succeeded = BulletTracePassed( node0.origin + aerial_offset, node1.origin + aerial_offset, false, undefined );
|
||||
}
|
||||
|
||||
if ( trace_succeeded )
|
||||
{
|
||||
Assert(!array_contains(node0.aerial_neighbors,node1));
|
||||
node0.aerial_neighbors[node0.aerial_neighbors.size] = node1;
|
||||
|
||||
Assert(!array_contains(node1.aerial_neighbors,node0));
|
||||
node1.aerial_neighbors[node1.aerial_neighbors.size] = node0;
|
||||
|
||||
if ( SCR_CONST_DEBUG_SHOW_ADDED_LINKS )
|
||||
level.added_aerial_links[level.added_aerial_links.size] = [node0,node1];
|
||||
}
|
||||
|
||||
if ( (num_traces % increment) == 0 )
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
potential_aerial_neighbor_nodes = undefined;
|
||||
|
||||
// Finally try to divide the nodes again, and then discard any remaining orphaned groups (small collections of nodes that have no links to anything else)
|
||||
node_groups = divide_nodes_into_groups( level.aerial_pathnodes );
|
||||
if ( SCR_CONST_DEBUG_SHOW_NODE_GROUPS )
|
||||
{
|
||||
// Sort the groups into largest to smallest, for consistent group coloring
|
||||
node_groups = array_sort_with_func( node_groups, ::is_group_a_larger_than_group_b );
|
||||
for( i=0; i<node_groups.size; i++ )
|
||||
{
|
||||
foreach( node in node_groups[i] )
|
||||
node.aerial_group = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't need to keep aerial_group stored on each pathnode anymore
|
||||
foreach( node in level.aerial_pathnodes )
|
||||
node.aerial_group = undefined;
|
||||
}
|
||||
|
||||
largest_group_size = 0;
|
||||
for ( i=0; i<node_groups.size; i++ )
|
||||
{
|
||||
largest_group_size = max(node_groups[i].size, largest_group_size);
|
||||
}
|
||||
|
||||
Assert( level.aerial_pathnodes.size < 40 || largest_group_size > 20 ); // Assert that either we have a small number of aerial nodes, or there's at least one large group of them
|
||||
for ( i=0; i<node_groups.size; i++ )
|
||||
{
|
||||
if ( node_groups[i].size < 0.1 * largest_group_size )
|
||||
{
|
||||
foreach( node in node_groups[i] )
|
||||
{
|
||||
// Remove the node from the aerial pathnode list
|
||||
level.aerial_pathnodes = array_remove( level.aerial_pathnodes, node );
|
||||
|
||||
// For each neighbor, find the link back to this node and sever it
|
||||
foreach( neighbor_node in node.aerial_neighbors )
|
||||
{
|
||||
for( j=0; j<neighbor_node.aerial_neighbors.size; j++ )
|
||||
{
|
||||
neighbor_node_neighbor = neighbor_node.aerial_neighbors[j];
|
||||
if ( neighbor_node_neighbor == node )
|
||||
{
|
||||
neighbor_node.aerial_neighbors[j] = neighbor_node.aerial_neighbors[neighbor_node.aerial_neighbors.size-1];
|
||||
neighbor_node.aerial_neighbors[neighbor_node.aerial_neighbors.size-1] = undefined;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now clear out all of this node's neighbors
|
||||
node.aerial_neighbors = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
level.calculated_aerial_nodes_done = true;
|
||||
level.calculated_aerial_nodes_in_progress = false;
|
||||
|
||||
/#
|
||||
thread draw_debug_aerial_nodes();
|
||||
#/
|
||||
}
|
||||
|
||||
is_group_a_larger_than_group_b( a, b )
|
||||
{
|
||||
return ( a.size > b.size );
|
||||
}
|
||||
|
||||
is_pair_a_closer_than_pair_b( a, b )
|
||||
{
|
||||
return ( a[2] < b[2] );
|
||||
}
|
||||
|
||||
num_node_connections_to_group( node0, group_index )
|
||||
{
|
||||
connections = 0;
|
||||
foreach( neighbor in node0.aerial_neighbors )
|
||||
{
|
||||
if ( neighbor.aerial_group == group_index )
|
||||
connections++;
|
||||
}
|
||||
|
||||
return connections;
|
||||
}
|
||||
|
||||
node0_has_neighbor_connected_to_node1( node0, node1 )
|
||||
{
|
||||
foreach( neighbor in node0.aerial_neighbors )
|
||||
{
|
||||
foreach( neighbor_neighbor in neighbor.aerial_neighbors )
|
||||
{
|
||||
if ( neighbor_neighbor == node1 )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
divide_nodes_into_groups( nodes, ignore_size )
|
||||
{
|
||||
if ( !IsDefined(ignore_size) )
|
||||
ignore_size = 0; // Ignore any groups with this size or less
|
||||
|
||||
foreach( node in nodes )
|
||||
node.aerial_group = undefined;
|
||||
|
||||
temp_aerial_nodes = nodes;
|
||||
node_groups = [];
|
||||
while( temp_aerial_nodes.size > 0 ) // While there's still nodes to process
|
||||
{
|
||||
current_index = node_groups.size;
|
||||
node_groups[current_index] = [];
|
||||
|
||||
temp_aerial_nodes[0].aerial_group = -1; // -1 for group means the node is waiting to be processed by the search
|
||||
open_nodes = [temp_aerial_nodes[0]];
|
||||
open_nodes_processed_for_this_group = 0;
|
||||
while( open_nodes.size > 0 )
|
||||
{
|
||||
// Add the current open node to the list, and mark its group
|
||||
current_open_node = open_nodes[0];
|
||||
node_groups[current_index][node_groups[current_index].size] = current_open_node;
|
||||
Assert(!IsDefined(current_open_node.aerial_group) || current_open_node.aerial_group == -1);
|
||||
current_open_node.aerial_group = current_index;
|
||||
|
||||
// Then remove it and add its undiscovered neighbors (since they are part of the same group)
|
||||
open_nodes[0] = open_nodes[open_nodes.size-1];
|
||||
open_nodes[open_nodes.size-1] = undefined;
|
||||
|
||||
foreach( neighbor_node in current_open_node.aerial_neighbors )
|
||||
{
|
||||
if ( !IsDefined(neighbor_node.aerial_group) )
|
||||
{
|
||||
neighbor_node.aerial_group = -1;
|
||||
open_nodes[open_nodes.size] = neighbor_node;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally remove it from the temp array since it has been processed
|
||||
for( i = 0; i < temp_aerial_nodes.size; i++ )
|
||||
{
|
||||
if ( temp_aerial_nodes[i] == current_open_node )
|
||||
{
|
||||
temp_aerial_nodes[i] = temp_aerial_nodes[temp_aerial_nodes.size-1];
|
||||
temp_aerial_nodes[temp_aerial_nodes.size-1] = undefined;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
open_nodes_processed_for_this_group++;
|
||||
if ( open_nodes_processed_for_this_group > 100 )
|
||||
{
|
||||
wait(0.05); // Wait a frame if we've processed a ton of open nodes in a single group
|
||||
open_nodes_processed_for_this_group = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( node_groups[current_index].size <= ignore_size )
|
||||
{
|
||||
node_groups[current_index] = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
|
||||
return node_groups;
|
||||
}
|
||||
|
||||
/#
|
||||
should_draw_for_node( node, max_dist_sq, player_origin, player_angles, player_fov )
|
||||
{
|
||||
if ( Distance2DSquared(player_origin,node.origin) > max_dist_sq )
|
||||
return false;
|
||||
|
||||
if ( !within_fov(player_origin, player_angles, (node.origin+get_aerial_offset()), player_fov) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
draw_debug_aerial_nodes()
|
||||
{
|
||||
self notify("bot_draw_debug_aerial_nodes");
|
||||
self endon("bot_draw_debug_aerial_nodes");
|
||||
level endon("teleport_to_zone");
|
||||
|
||||
node_colors = [ (0,1,1), (0,1,0), (0,0,1), (1,0,1), (1,1,0), (1,0,0), (1,0.6,0.6), (0.6,1,0.6), (0.6,0.6,1), (0.1,0.1,0.1) ];
|
||||
draw_time = 0.5;
|
||||
|
||||
while(1)
|
||||
{
|
||||
ai_showNodesAerial = GetDvar( "ai_showNodesAerial" );
|
||||
if ( ai_showNodesAerial == "1" || ai_showNodesAerial == "2" )
|
||||
{
|
||||
max_dist_sq = squared(GetDvarFloat("ai_ShowNodesDist"));
|
||||
player = maps\mp\gametypes\_dev::getNotBot();
|
||||
aerial_offset = get_aerial_offset();
|
||||
if ( IsDefined(player) && max_dist_sq > 0 )
|
||||
{
|
||||
if ( IsDefined(level.aerial_pathnodes_force_connect) )
|
||||
{
|
||||
foreach( override in level.aerial_pathnodes_force_connect )
|
||||
maps\mp\bots\_bots_util::bot_draw_cylinder( override.origin - (0,0,500), override.radius, 1000, draw_time, undefined, (1,0,0), true, 20 );
|
||||
}
|
||||
|
||||
player_origin = player GetViewOrigin();
|
||||
player_angles = player GetPlayerAngles();
|
||||
fov = cos( 85 * 0.5 );
|
||||
|
||||
current_nodes = level.aerial_pathnodes;
|
||||
for ( i = 0; i < current_nodes.size; i++ )
|
||||
{
|
||||
if ( should_draw_for_node(current_nodes[i], max_dist_sq, player_origin, player_angles, fov) )
|
||||
{
|
||||
if ( SCR_CONST_DEBUG_SHOW_NODE_GROUPS )
|
||||
{
|
||||
color_index = current_nodes[i].aerial_group % 10;
|
||||
color = node_colors[color_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
color = (0,1,1);
|
||||
}
|
||||
|
||||
if ( ai_showNodesAerial == "2" )
|
||||
{
|
||||
maps\mp\bots\_bots_util::bot_draw_cylinder(current_nodes[i].origin - (0,0,5) + aerial_offset, 10, 12, draw_time, undefined, color, true, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach( neighbor_node in current_nodes[i].aerial_neighbors )
|
||||
{
|
||||
// Neighbor node is valid and has not been visited yet, so draw a line to it
|
||||
if ( array_contains(current_nodes,neighbor_node) )
|
||||
{
|
||||
if ( SCR_CONST_DEBUG_SHOW_ADDED_LINKS && !SCR_CONST_DEBUG_DONT_CONNECT_GROUPS )
|
||||
{
|
||||
foreach( node_pair in level.added_aerial_links )
|
||||
{
|
||||
if ( node_pair[0] == current_nodes[i] && node_pair[1] == neighbor_node || node_pair[0] == neighbor_node && node_pair[1] == current_nodes[i] )
|
||||
{
|
||||
color = (1,0,0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line( current_nodes[i].origin + aerial_offset, neighbor_node.origin + aerial_offset, color, 1.0, true, INT(draw_time * 20) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once we've gone through all the neighbor nodes, remove this node from the list (so we don't get double lines)
|
||||
current_nodes[i] = current_nodes[current_nodes.size-1];
|
||||
current_nodes[current_nodes.size-1] = undefined;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait(draw_time);
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: node_is_aerial(<node>)"
|
||||
"Summary: Returns true if this node is an aerial node"
|
||||
"MandatoryArg: <node> : The node to check"
|
||||
"Example: if ( node_is_aerial(node1) )"
|
||||
///ScriptDocEnd
|
||||
============
|
||||
*/
|
||||
node_is_aerial( node )
|
||||
{
|
||||
return IsDefined(node.aerial_neighbors);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: get_ent_closest_aerial_node(<max_radius>, <min_radius>)"
|
||||
"Summary: Finds the closest aerial node to the given entity"
|
||||
"CallOn: An entity"
|
||||
"OptionalArg: <max_radius> : Maximum distance to search"
|
||||
"OptionalArg: <min_radius> : Minimum distance to search"
|
||||
"Example: closest_node = self get_closest_aerial_node();"
|
||||
///ScriptDocEnd
|
||||
============
|
||||
*/
|
||||
get_ent_closest_aerial_node( max_radius, min_radius )
|
||||
{
|
||||
if ( !IsDefined(max_radius) )
|
||||
max_radius = 1500;
|
||||
if ( !IsDefined(min_radius) )
|
||||
min_radius = 0;
|
||||
|
||||
nodes = GetNodesInRadiusSorted( self.origin, max_radius, min_radius, get_aerial_offset()[2] * 2, "path" );
|
||||
for( i=0; i<nodes.size; i++ )
|
||||
{
|
||||
if ( node_is_aerial(nodes[i]) )
|
||||
return nodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: find_path_between_aerial_nodes(<node_start>, <node_end>)"
|
||||
"Summary: Finds a (potentially non-optimal) path between the aerial start and end nodes"
|
||||
"MandatoryArg: <node_start> : The start node"
|
||||
"MandatoryArg: <node_end> : The end node"
|
||||
"Example: path = find_path_between_aerial_nodes(node1, node2);"
|
||||
///ScriptDocEnd
|
||||
============
|
||||
*/
|
||||
find_path_between_aerial_nodes( node_start, node_end )
|
||||
{
|
||||
Assert( node_is_aerial(node_start) );
|
||||
Assert( node_is_aerial(node_end) );
|
||||
|
||||
node_start.path_to_this_node = [];
|
||||
node_queue = [node_start];
|
||||
all_nodes_explored = [node_start];
|
||||
|
||||
while(!IsDefined(node_end.path_to_this_node))
|
||||
{
|
||||
current_node = node_queue[0];
|
||||
node_queue = array_remove(node_queue,current_node);
|
||||
|
||||
foreach( neighbor_node in current_node.aerial_neighbors )
|
||||
{
|
||||
if ( !IsDefined(neighbor_node.path_to_this_node) )
|
||||
{
|
||||
neighbor_node.path_to_this_node = array_add(current_node.path_to_this_node,current_node);
|
||||
node_queue[node_queue.size] = neighbor_node;
|
||||
all_nodes_explored[all_nodes_explored.size] = neighbor_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path = array_add(node_end.path_to_this_node,node_end);
|
||||
foreach( node in all_nodes_explored )
|
||||
node.path_to_this_node = undefined;
|
||||
|
||||
return path;
|
||||
}
|
64
raw/maps/mp/_animatedmodels.gsc
Normal file
64
raw/maps/mp/_animatedmodels.gsc
Normal file
@ -0,0 +1,64 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
#using_animtree( "animated_props" );
|
||||
main()
|
||||
{
|
||||
//level.init_animatedmodels_dump = false;
|
||||
|
||||
if ( !isdefined( level.anim_prop_models ) )
|
||||
level.anim_prop_models = []; // this is what the LD puts in their map
|
||||
|
||||
// Do special MP anim precaching
|
||||
model_keys = GetArrayKeys( level.anim_prop_models );
|
||||
foreach ( model_key in model_keys )
|
||||
{
|
||||
anim_keys = GetArrayKeys( level.anim_prop_models[model_key] );
|
||||
foreach ( anim_key in anim_keys )
|
||||
PrecacheMpAnim( level.anim_prop_models[model_key][anim_key] );
|
||||
//PrecacheMpAnim( level.anim_prop_models[ "foliage_tree_palm_bushy_1" ][ "strong" ] );
|
||||
}
|
||||
|
||||
// wait until the end of the frame so that maps can init their trees
|
||||
// in their _anim instead of only above _load
|
||||
waittillframeend;
|
||||
|
||||
level.init_animatedmodels = [];
|
||||
|
||||
animated_models = getentarray( "animated_model", "targetname" );
|
||||
//array_thread( animated_models, ::model_init );
|
||||
|
||||
// one or more of the models initialized by model_init() was not setup by the map
|
||||
// so print this helpful note so the designer can see how to add it ot their level
|
||||
//if ( level.init_animatedmodels_dump )
|
||||
//assertmsg( "anims not cached for animated prop model, Repackage Zones and Rebuild Precache Script in Launcher:" );
|
||||
|
||||
array_thread( animated_models, ::animateModel );
|
||||
|
||||
level.init_animatedmodels = undefined;
|
||||
}
|
||||
|
||||
// Disabled for now since we are precaching .animation prefabs in a non-standard way
|
||||
/*model_init()
|
||||
{
|
||||
if ( !isdefined( level.anim_prop_models[ self.model ] ) )
|
||||
level.init_animatedmodels_dump = true;
|
||||
}*/
|
||||
|
||||
// TODO: When we have multiple animations, instead of choosing randomly, do round-robin to get an even spread
|
||||
animateModel()
|
||||
{
|
||||
if ( IsDefined( self.animation ) )
|
||||
{
|
||||
animation = self.animation;
|
||||
}
|
||||
else
|
||||
{
|
||||
keys = GetArrayKeys( level.anim_prop_models[ self.model ] );
|
||||
animkey = keys[ RandomInt( keys.size ) ];
|
||||
animation = level.anim_prop_models[ self.model ][ animkey ];
|
||||
}
|
||||
|
||||
//wait( RandomFloatRange( 0, 5 ) ); // TODO: get a way to play animations at random starting points
|
||||
self ScriptModelPlayAnim( animation );
|
||||
self willNeverChange();
|
||||
}
|
98
raw/maps/mp/_areas.gsc
Normal file
98
raw/maps/mp/_areas.gsc
Normal file
@ -0,0 +1,98 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*QUAKED trigger_multiple_area (0.12 0.23 1.0) ?
|
||||
defaulttexture="trigger"
|
||||
"script_area" - A localized string that names the area. e.g. "MP_FLOWER_SHOP"
|
||||
Defines an area that the player is in.*/
|
||||
|
||||
/*QUAKED trigger_multiple_softlanding (0.12 0.23 1.0) ?
|
||||
defaulttexture="trigger"
|
||||
"script_type" - "car", "boxes", "trash"
|
||||
Defines a soft landing area.*/
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
level.softLandingTriggers = getEntArray( "trigger_multiple_softlanding", "classname" );
|
||||
|
||||
destructibles = getEntArray( "destructible_vehicle", "targetname" );
|
||||
|
||||
foreach ( trigger in level.softLandingTriggers )
|
||||
{
|
||||
if ( trigger.script_type != "car" )
|
||||
continue;
|
||||
|
||||
foreach ( destructible in destructibles )
|
||||
{
|
||||
/*
|
||||
if ( !trigger isTouching( destructible ) )
|
||||
{
|
||||
println( distance( trigger.origin, destructible.origin ) );
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( distance( trigger.origin, destructible.origin ) > 64.0 )
|
||||
continue;
|
||||
|
||||
assert( !isDefined( trigger.destructible ) );
|
||||
|
||||
trigger.destructible = destructible;
|
||||
}
|
||||
}
|
||||
|
||||
//foreach ( trigger in level.softLandingTriggers )
|
||||
// trigger thread common_scripts\_dynamic_world::triggerTouchThink( ::playerEnterSoftLanding, ::playerLeaveSoftLanding );
|
||||
|
||||
thread onPlayerConnect();
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "connected", player );
|
||||
|
||||
player.softLanding = undefined;
|
||||
|
||||
player thread softLandingWaiter();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
playerEnterSoftLanding( trigger )
|
||||
{
|
||||
self.softLanding = trigger;
|
||||
}
|
||||
|
||||
|
||||
playerLeaveSoftLanding( trigger )
|
||||
{
|
||||
self.softLanding = undefined;
|
||||
}
|
||||
|
||||
|
||||
softLandingWaiter()
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill ( "soft_landing", trigger, damage );
|
||||
|
||||
//if ( damage < 10 )
|
||||
// continue;
|
||||
|
||||
if ( !isDefined( trigger.destructible ) )
|
||||
continue;
|
||||
|
||||
//magicBullet( "mp5_mp", self.origin, self.origin + (0,0,-100), self );
|
||||
|
||||
//self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, dflags );
|
||||
|
||||
//traceData = bulletTrace( self.origin, self.origin + (0,0,-100), true, self );
|
||||
|
||||
}
|
||||
}
|
829
raw/maps/mp/_art.gsc
Normal file
829
raw/maps/mp/_art.gsc
Normal file
@ -0,0 +1,829 @@
|
||||
// This function should take care of grain and glow settings for each map, plus anything else that artists
|
||||
// need to be able to tweak without bothering level designers.
|
||||
#include common_scripts\utility;
|
||||
#include common_scripts\_artCommon;
|
||||
|
||||
main()
|
||||
{
|
||||
if ( !isDefined( level.dofDefault ) )
|
||||
{
|
||||
//These values match what the code is setting ClientSpawn
|
||||
level.dofDefault[ "nearStart" ] = 0;
|
||||
level.dofDefault[ "nearEnd" ] = 0;
|
||||
level.dofDefault[ "farStart" ] = 0;
|
||||
level.dofDefault[ "farEnd" ] = 0;
|
||||
level.dofDefault[ "nearBlur" ] = 6;
|
||||
level.dofDefault[ "farBlur" ] = 1.8;
|
||||
}
|
||||
/$
|
||||
if ( !isdefined( level.script ) )
|
||||
level.script = ToLower( GetDvar( "mapname" ) );
|
||||
|
||||
setDevDvarIfUninitialized( "scr_art_tweak", 0 );
|
||||
setDevDvarIfUninitialized( "scr_dof_enable", "1" );
|
||||
setDevDvarIfUninitialized( "scr_cmd_plr_sun", "0" );
|
||||
setDevDvarIfUninitialized( "scr_cmd_plr_sun_atmos_fog", "0" );
|
||||
setDevDvarIfUninitialized( "scr_cinematic_autofocus", "1" );
|
||||
setDevDvarIfUninitialized( "scr_art_visionfile", level.script );
|
||||
|
||||
level.curDoF = ( level.dofDefault[ "farStart" ] - level.dofDefault[ "nearEnd" ] ) / 2;
|
||||
level._clearalltextafterhudelem = false;
|
||||
level.buttons = [];
|
||||
|
||||
if( !isdefined( level.vision_set_vision ) )
|
||||
{
|
||||
level.vision_set_vision = [];
|
||||
}
|
||||
|
||||
if ( !isdefined( level.vision_set_transition_ent ) )
|
||||
{
|
||||
level.vision_set_transition_ent = SpawnStruct();
|
||||
level.vision_set_transition_ent.vision_set = level.script;
|
||||
level.vision_set_transition_ent.time = 0;
|
||||
}
|
||||
|
||||
thread tweaklightset();
|
||||
thread tweakart();
|
||||
|
||||
$/
|
||||
}
|
||||
|
||||
setup_fog_tweak()
|
||||
{
|
||||
/$
|
||||
if( !isdefined( level.vision_set_fog ) )
|
||||
{
|
||||
level.vision_set_fog = [];
|
||||
if (isdefined(level._art_fog_setup))
|
||||
[[level._art_fog_setup]]();
|
||||
construct_vision_set( "" ); // so we always have an entry for the default case
|
||||
construct_vision_set( level.script );
|
||||
set_fog( level.script );
|
||||
common_scripts\_artCommon::setfogsliders();
|
||||
}
|
||||
$/
|
||||
}
|
||||
|
||||
initTweaks()
|
||||
{
|
||||
/$
|
||||
setup_fog_tweak();
|
||||
// create new vision sets for those triggers that aren't yet hooked up
|
||||
construct_vision_ents();
|
||||
|
||||
if ( !IsDefined( level.vision_set_names ) )
|
||||
level.vision_set_names = [];
|
||||
|
||||
// Add existing vision sets for any fog that's been created thus far
|
||||
foreach( key, value in level.vision_set_fog )
|
||||
{
|
||||
common_scripts\_artCommon::add_vision_set_to_list( key );
|
||||
}
|
||||
|
||||
add_vision_sets_from_triggers();
|
||||
|
||||
hud_init();
|
||||
|
||||
playerInit();
|
||||
|
||||
level.players[0] VisionSetNakedForPlayer( level.script, 0 ); // to disable clientside visionsets, so the script based fog is active
|
||||
$/
|
||||
}
|
||||
|
||||
tweaklightset()
|
||||
{
|
||||
/$
|
||||
SetDevDvar( "scr_lightset_dump", "0" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if ( GetDvarInt( "scr_lightset_dump" ) != 0 )
|
||||
{
|
||||
SetDevDvar( "scr_lightset_dump", "0" );
|
||||
common_scripts\_artCommon::print_lightset( get_lightset_filename() );
|
||||
}
|
||||
|
||||
wait .05;
|
||||
}
|
||||
$/
|
||||
}
|
||||
|
||||
tweakart()
|
||||
{
|
||||
/$
|
||||
if ( !isdefined( level.tweakfile ) )
|
||||
level.tweakfile = false;
|
||||
|
||||
// not in DEVGUI
|
||||
SetDevDvar( "scr_fog_fraction", "1.0" );
|
||||
SetDevDvar( "scr_art_dump", "0" );
|
||||
|
||||
// update the devgui variables to current settings
|
||||
SetDevDvar( "scr_dof_nearStart", level.dofDefault[ "nearStart" ] );
|
||||
SetDevDvar( "scr_dof_nearEnd", level.dofDefault[ "nearEnd" ] );
|
||||
SetDevDvar( "scr_dof_farStart", level.dofDefault[ "farStart" ] );
|
||||
SetDevDvar( "scr_dof_farEnd", level.dofDefault[ "farEnd" ] );
|
||||
SetDevDvar( "scr_dof_nearBlur", level.dofDefault[ "nearBlur" ] );
|
||||
SetDevDvar( "scr_dof_farBlur", level.dofDefault[ "farBlur" ] );
|
||||
|
||||
// not in DEVGUI
|
||||
level.fogfraction = 1.0;
|
||||
|
||||
file = undefined;
|
||||
filename = undefined;
|
||||
last_vision_set = "";
|
||||
|
||||
inited = false;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
while ( GetDvarInt( "scr_art_tweak", 0 ) == 0 )
|
||||
{
|
||||
AssertEx( GetDvarInt( "scr_art_dump", 0 ) == 0, "Must Enable Art Tweaks to export _art file." );
|
||||
wait .05;
|
||||
if ( ! GetDvarInt( "scr_art_tweak", 0 ) == 0 )
|
||||
common_scripts\_artCommon::setfogsliders();// sets the sliders to whatever the current fog value is
|
||||
}
|
||||
|
||||
if ( GetDvarInt( "scr_art_tweak_message" ) )
|
||||
{
|
||||
SetDevDvar( "scr_art_tweak_message", "0" );
|
||||
IPrintLnBold( "ART TWEAK ENABLED" );
|
||||
}
|
||||
if ( !inited )
|
||||
{
|
||||
inited = true;
|
||||
initTweaks();
|
||||
}
|
||||
|
||||
//translate the slider values to script variables
|
||||
common_scripts\_artCommon::translateFogSlidersToScript();
|
||||
|
||||
// dofvarupdate();
|
||||
|
||||
// catch all those cases where a slider can be pushed to a place of conflict
|
||||
fovslidercheck();
|
||||
|
||||
common_scripts\_artCommon::fogslidercheck();
|
||||
|
||||
|
||||
dump = dumpsettings();// dumps and returns true if the dump dvar is set
|
||||
|
||||
updateFogEntFromScript();
|
||||
|
||||
|
||||
if ( getdvarint( "scr_select_art_next" ) || button_down( "dpad_up", "kp_uparrow" ) )
|
||||
setgroup_down();
|
||||
else if ( getdvarint( "scr_select_art_prev" ) || button_down( "dpad_down", "kp_downarrow" ) )
|
||||
setgroup_up();
|
||||
else if( level.vision_set_transition_ent.vision_set != last_vision_set )
|
||||
{
|
||||
last_vision_set = level.vision_set_transition_ent.vision_set;
|
||||
setcurrentgroup( last_vision_set );
|
||||
}
|
||||
|
||||
// level.player setDefaultDepthOfField();
|
||||
if ( dump )
|
||||
{
|
||||
IPrintLnBold( "Art settings dumped success!" );
|
||||
SetdevDvar( "scr_art_dump", "0" );
|
||||
}
|
||||
wait .1;
|
||||
}
|
||||
$/
|
||||
}
|
||||
|
||||
fovslidercheck()
|
||||
{
|
||||
/$
|
||||
// catch all those cases where a slider can be pushed to a place of conflict
|
||||
if ( level.dofDefault[ "nearStart" ] >= level.dofDefault[ "nearEnd" ] )
|
||||
{
|
||||
level.dofDefault[ "nearStart" ] = level.dofDefault[ "nearEnd" ] - 1;
|
||||
SetDevDvar( "scr_dof_nearStart", level.dofDefault[ "nearStart" ] );
|
||||
}
|
||||
if ( level.dofDefault[ "nearEnd" ] <= level.dofDefault[ "nearStart" ] )
|
||||
{
|
||||
level.dofDefault[ "nearEnd" ] = level.dofDefault[ "nearStart" ] + 1;
|
||||
SetDevDvar( "scr_dof_nearEnd", level.dofDefault[ "nearEnd" ] );
|
||||
}
|
||||
if ( level.dofDefault[ "farStart" ] >= level.dofDefault[ "farEnd" ] )
|
||||
{
|
||||
level.dofDefault[ "farStart" ] = level.dofDefault[ "farEnd" ] - 1;
|
||||
SetDevDvar( "scr_dof_farStart", level.dofDefault[ "farStart" ] );
|
||||
}
|
||||
if ( level.dofDefault[ "farEnd" ] <= level.dofDefault[ "farStart" ] )
|
||||
{
|
||||
level.dofDefault[ "farEnd" ] = level.dofDefault[ "farStart" ] + 1;
|
||||
SetDevDvar( "scr_dof_farEnd", level.dofDefault[ "farEnd" ] );
|
||||
}
|
||||
if ( level.dofDefault[ "farBlur" ] >= level.dofDefault[ "nearBlur" ] )
|
||||
{
|
||||
level.dofDefault[ "farBlur" ] = level.dofDefault[ "nearBlur" ] - .1;
|
||||
SetDevDvar( "scr_dof_farBlur", level.dofDefault[ "farBlur" ] );
|
||||
}
|
||||
if ( level.dofDefault[ "farStart" ] <= level.dofDefault[ "nearEnd" ] )
|
||||
{
|
||||
level.dofDefault[ "farStart" ] = level.dofDefault[ "nearEnd" ] + 1;
|
||||
SetDevDvar( "scr_dof_farStart", level.dofDefault[ "farStart" ] );
|
||||
}
|
||||
$/
|
||||
}
|
||||
|
||||
// updateFogEntFromScript()
|
||||
// NOTE: This has moved to _artCommon.gsc
|
||||
|
||||
construct_vision_ents()
|
||||
{
|
||||
if( !isdefined( level.vision_set_fog ))
|
||||
level.vision_set_fog = [];
|
||||
trigger_multiple_visionsets = GetEntArray( "trigger_multiple_visionset" , "classname" );
|
||||
|
||||
foreach( trigger in trigger_multiple_visionsets )
|
||||
{
|
||||
if( IsDefined( trigger.script_visionset ) )
|
||||
{
|
||||
construct_vision_set( trigger.script_visionset );
|
||||
}
|
||||
|
||||
if ( IsDefined( trigger.script_visionset_start ) )
|
||||
{
|
||||
construct_vision_set( trigger.script_visionset_start );
|
||||
}
|
||||
|
||||
if ( IsDefined( trigger.script_visionset_end ) )
|
||||
{
|
||||
construct_vision_set( trigger.script_visionset_end );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
construct_vision_set( vision_set )
|
||||
{
|
||||
if ( IsDefined( level.vision_set_fog[ vision_set ] ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
create_default_vision_set_fog( vision_set );
|
||||
create_vision_set_vision( vision_set );
|
||||
|
||||
IPrintLnBold( "new vision: " + vision_set );
|
||||
}
|
||||
|
||||
create_vision_set_vision( vision )
|
||||
{
|
||||
if ( !isdefined( level.vision_set_vision ) )
|
||||
level.vision_set_vision = [];
|
||||
ent = SpawnStruct();
|
||||
ent.name = vision;
|
||||
|
||||
level.vision_set_vision[ vision ] = ent;
|
||||
return ent;
|
||||
}
|
||||
|
||||
add_vision_sets_from_triggers()
|
||||
{
|
||||
/$
|
||||
assert( IsDefined( level.vision_set_fog ) );
|
||||
|
||||
triggers = GetEntArray( "trigger_multiple_visionset" , "classname" );
|
||||
|
||||
foreach( trigger in triggers )
|
||||
{
|
||||
name = undefined;
|
||||
|
||||
if( IsDefined( trigger.script_visionset ) )
|
||||
name = ToLower( trigger.script_visionset );
|
||||
else if ( IsDefined( trigger.script_visionset_start ) )
|
||||
name = ToLower( trigger.script_visionset_start );
|
||||
else if ( IsDefined( trigger.script_visionset_end ) )
|
||||
name = ToLower( trigger.script_visionset_end );
|
||||
if ( IsDefined( name ) )
|
||||
add_vision_set( name );
|
||||
}
|
||||
$/
|
||||
}
|
||||
|
||||
add_vision_set( vision_set_name )
|
||||
{
|
||||
/$
|
||||
assert( vision_set_name == ToLower( vision_set_name ) );
|
||||
|
||||
if ( IsDefined( level.vision_set_fog[ vision_set_name ] ) )
|
||||
return;
|
||||
|
||||
create_default_vision_set_fog( vision_set_name );
|
||||
common_scripts\_artCommon::add_vision_set_to_list( vision_set_name );
|
||||
|
||||
IPrintLnBold( "new vision: " + vision_set_name );
|
||||
$/
|
||||
}
|
||||
|
||||
create_default_vision_set_fog( name )
|
||||
{
|
||||
ent = create_vision_set_fog(name);
|
||||
ent.startDist = 3764.17;
|
||||
ent.halfwayDist = 19391;
|
||||
ent.red = 0.661137;
|
||||
ent.green = 0.554261;
|
||||
ent.blue = 0.454014;
|
||||
ent.maxOpacity = 0.7;
|
||||
ent.transitionTime = 0;
|
||||
ent.skyFogIntensity = 0;
|
||||
ent.skyFogMinAngle = 0;
|
||||
ent.skyFogMaxAngle = 0;
|
||||
ent.heightFogEnabled = 0;
|
||||
ent.heightFogBaseHeight = 0;
|
||||
ent.heightFogHalfPlaneDistance = 1000;
|
||||
}
|
||||
|
||||
create_vision_set_fog( fogset )
|
||||
{
|
||||
if ( !isdefined( level.vision_set_fog ) )
|
||||
level.vision_set_fog = [];
|
||||
ent = SpawnStruct();
|
||||
ent.name = fogset;
|
||||
|
||||
|
||||
/* // Use HDRColorIntensity/HDRSunColorIntensity existence to determine the space of the color values.
|
||||
|
||||
// Special init for variables that may not exist on every set of fog yet -- add variable defaults here to avoid IsDefined checks everywhere later on
|
||||
ent.HDRColorIntensity = 1;
|
||||
ent.HDRSunColorIntensity = 1;
|
||||
*/
|
||||
ent.skyFogIntensity = 0;
|
||||
ent.skyFogMinAngle = 0;
|
||||
ent.skyFogMaxAngle = 0;
|
||||
ent.heightFogEnabled = 0;
|
||||
ent.heightFogBaseHeight = 0;
|
||||
ent.heightFogHalfPlaneDistance = 1000;
|
||||
|
||||
level.vision_set_fog[ ToLower(fogset) ] = ent;
|
||||
return ent;
|
||||
}
|
||||
|
||||
set_fog( fogname, transition_time )
|
||||
{
|
||||
level.vision_set_transition_ent.vision_set = fogname;
|
||||
level.vision_set_transition_ent.time = transition_time;
|
||||
ent = get_fog(fogname);
|
||||
if( GetDvarInt( "scr_art_tweak") != 0 )
|
||||
{
|
||||
translateEntTosliders(ent );
|
||||
//pending some mechanism to handle transition while your editting, just set to zero for now since it leaves less room for ugly.
|
||||
transition_time = 0;
|
||||
}
|
||||
set_fog_to_ent_values( ent, transition_time );
|
||||
}
|
||||
|
||||
|
||||
translateEntTosliders(ent)
|
||||
{
|
||||
/$
|
||||
ConvertLegacyFog( ent );
|
||||
SetDevDvar( "scr_fog_exp_halfplane", ent.halfwayDist );
|
||||
SetDevDvar( "scr_fog_nearplane", ent.startDist);
|
||||
SetDevDvar( "scr_fog_color", ( ent.red, ent.green, ent.blue ) );
|
||||
SetDevDvar( "scr_fog_color_intensity", ent.HDRColorIntensity );
|
||||
SetDevDvar( "scr_fog_max_opacity", ent.maxOpacity );
|
||||
SetDevDvar( "scr_skyFogIntensity", ent.skyFogIntensity );
|
||||
SetDevDvar( "scr_skyFogMinAngle", ent.skyFogMinAngle );
|
||||
SetDevDvar( "scr_skyFogMaxAngle", ent.skyFogMaxAngle );
|
||||
SetDevDvar( "scr_heightFogEnabled", ent.heightFogEnabled );
|
||||
SetDevDvar( "scr_heightFogBaseHeight", ent.heightFogBaseHeight );
|
||||
SetDevDvar( "scr_heightFogHalfPlaneDistance", ent.heightFogHalfPlaneDistance );
|
||||
|
||||
if ( IsDefined( ent.sunFogEnabled ) && ent.sunFogEnabled)
|
||||
{
|
||||
SetDevDvar( "scr_sunFogEnabled", 1 );
|
||||
SetDevDvar( "scr_sunFogColor", (ent.sunRed, ent.sunGreen,ent.sunBlue) );
|
||||
SetDevDvar( "scr_sunFogColorIntensity", ent.HDRSunColorIntensity );
|
||||
SetDevDvar( "scr_sunFogDir", ent.sunDir );
|
||||
SetDevDvar( "scr_sunFogBeginFadeAngle", ent.sunBeginFadeAngle );
|
||||
SetDevDvar( "scr_sunFogEndFadeAngle", ent.sunEndFadeAngle );
|
||||
SetDevDvar( "scr_sunFogScale", ent.normalFogScale );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDevDvar( "scr_sunFogEnabled", 0 );
|
||||
}
|
||||
|
||||
if ( IsDefined( ent.atmosFogEnabled ) )
|
||||
{
|
||||
AssertEx( IsDefined( ent.atmosFogSunFogColor ) );
|
||||
AssertEx( IsDefined( ent.atmosFogHazeColor ) );
|
||||
AssertEx( IsDefined( ent.atmosFogHazeStrength ) );
|
||||
AssertEx( IsDefined( ent.atmosFogHazeSpread ) );
|
||||
AssertEx( IsDefined( ent.atmosFogExtinctionStrength ) );
|
||||
AssertEx( IsDefined( ent.atmosFogInScatterStrength ) );
|
||||
AssertEx( IsDefined( ent.atmosFogHalfPlaneDistance ) );
|
||||
AssertEx( IsDefined( ent.atmosFogStartDistance ) );
|
||||
AssertEx( IsDefined( ent.atmosFogDistanceScale ) );
|
||||
AssertEx( IsDefined( ent.atmosFogSkyDistance ) );
|
||||
AssertEx( IsDefined( ent.atmosFogSkyAngularFalloffEnabled ) );
|
||||
AssertEx( IsDefined( ent.atmosFogSkyFalloffStartAngle ) );
|
||||
AssertEx( IsDefined( ent.atmosFogSkyFalloffAngleRange ) );
|
||||
AssertEx( IsDefined( ent.atmosFogSunDirection ) );
|
||||
AssertEx( IsDefined( ent.atmosFogHeightFogEnabled ) );
|
||||
AssertEx( IsDefined( ent.atmosFogHeightFogBaseHeight ) );
|
||||
AssertEx( IsDefined( ent.atmosFogHeightFogHalfPlaneDistance ) );
|
||||
|
||||
SetDevDvar( "scr_atmosFogEnabled", ent.atmosFogEnabled );
|
||||
SetDevDvar( "scr_atmosFogSunFogColor", ent.atmosFogSunFogColor );
|
||||
SetDevDvar( "scr_atmosFogHazeColor", ent.atmosFogHazeColor );
|
||||
SetDevDvar( "scr_atmosFogHazeStrength", ent.atmosFogHazeStrength );
|
||||
SetDevDvar( "scr_atmosFogHazeSpread", ent.atmosFogHazeSpread );
|
||||
SetDevDvar( "scr_atmosFogExtinctionStrength", ent.atmosFogExtinctionStrength );
|
||||
SetDevDvar( "scr_atmosFogInScatterStrength", ent.atmosFogInScatterStrength );
|
||||
SetDevDvar( "scr_atmosFogHalfPlaneDistance", ent.atmosFogHalfPlaneDistance );
|
||||
SetDevDvar( "scr_atmosFogStartDistance", ent.atmosFogStartDistance );
|
||||
SetDevDvar( "scr_atmosFogDistanceScale", ent.atmosFogDistanceScale );
|
||||
SetDevDvar( "scr_atmosFogSkyDistance", int( ent.atmosFogSkyDistance ) );
|
||||
SetDevDvar( "scr_atmosFogSkyAngularFalloffEnabled", ent.atmosFogSkyAngularFalloffEnabled );
|
||||
SetDevDvar( "scr_atmosFogSkyFalloffStartAngle", ent.atmosFogSkyFalloffStartAngle );
|
||||
SetDevDvar( "scr_atmosFogSkyFalloffAngleRange", ent.atmosFogSkyFalloffAngleRange );
|
||||
SetDevDvar( "scr_atmosFogSunDirection", ent.atmosFogSunDirection );
|
||||
SetDevDvar( "scr_atmosFogHeightFogEnabled", ent.atmosFogHeightFogEnabled );
|
||||
SetDevDvar( "scr_atmosFogHeightFogBaseHeight", ent.atmosFogHeightFogBaseHeight );
|
||||
SetDevDvar( "scr_atmosFogHeightFogHalfPlaneDistance", ent.atmosFogHeightFogHalfPlaneDistance );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDevDvar( "scr_atmosFogEnabled", false );
|
||||
}
|
||||
$/
|
||||
}
|
||||
|
||||
|
||||
hud_init()
|
||||
{
|
||||
listsize = 7;
|
||||
|
||||
hudelems = [];
|
||||
spacer = 15;
|
||||
div = int( listsize / 2 );
|
||||
org = 240 + div * spacer;
|
||||
alphainc = .5 / div;
|
||||
alpha = alphainc;
|
||||
|
||||
for ( i = 0;i < listsize;i++ )
|
||||
{
|
||||
hudelems[ i ] = _newhudelem();
|
||||
hudelems[ i ].location = 0;
|
||||
hudelems[ i ].alignX = "left";
|
||||
hudelems[ i ].alignY = "middle";
|
||||
hudelems[ i ].foreground = 1;
|
||||
hudelems[ i ].fontScale = 2;
|
||||
hudelems[ i ].sort = 20;
|
||||
if ( i == div )
|
||||
hudelems[ i ].alpha = 1;
|
||||
else
|
||||
hudelems[ i ].alpha = alpha;
|
||||
|
||||
hudelems[ i ].x = 20;
|
||||
hudelems[ i ].y = org;
|
||||
hudelems[ i ] _settext( "." );
|
||||
|
||||
if ( i == div )
|
||||
alphainc *= -1;
|
||||
|
||||
alpha += alphainc;
|
||||
|
||||
org -= spacer;
|
||||
}
|
||||
|
||||
level.spam_group_hudelems = hudelems;
|
||||
}
|
||||
|
||||
|
||||
_newhudelem()
|
||||
{
|
||||
if ( !isdefined( level.scripted_elems ) )
|
||||
level.scripted_elems = [];
|
||||
elem = newhudelem();
|
||||
level.scripted_elems[ level.scripted_elems.size ] = elem;
|
||||
return elem;
|
||||
}
|
||||
|
||||
_settext( text )
|
||||
{
|
||||
self.realtext = text;
|
||||
self settext( "_" );
|
||||
self thread _clearalltextafterhudelem();
|
||||
sizeofelems = 0;
|
||||
foreach ( elem in level.scripted_elems )
|
||||
{
|
||||
if ( isdefined( elem.realtext ) )
|
||||
{
|
||||
sizeofelems += elem.realtext.size;
|
||||
elem settext( elem.realtext );
|
||||
}
|
||||
}
|
||||
println( "Size of elems: " + sizeofelems );
|
||||
}
|
||||
|
||||
_clearalltextafterhudelem()
|
||||
{
|
||||
if (GetDvar("netconststrings_enabled") != "0")
|
||||
return;
|
||||
if ( level._clearalltextafterhudelem )
|
||||
return;
|
||||
level._clearalltextafterhudelem = true;
|
||||
self clearalltextafterhudelem();
|
||||
wait .05;
|
||||
level._clearalltextafterhudelem = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
setgroup_up()
|
||||
{
|
||||
reset_cmds();
|
||||
index = undefined;
|
||||
keys = getarraykeys( level.vision_set_fog );
|
||||
for ( i = 0;i < keys.size;i++ )
|
||||
if ( keys[ i ] == level.vision_set_transition_ent.vision_set )
|
||||
{
|
||||
index = i + 1;
|
||||
break;
|
||||
}
|
||||
if ( index == keys.size )
|
||||
return;
|
||||
|
||||
setcurrentgroup( keys[index] );
|
||||
}
|
||||
|
||||
setgroup_down()
|
||||
{
|
||||
reset_cmds();
|
||||
index = undefined;
|
||||
keys = getarraykeys( level.vision_set_fog );
|
||||
for ( i = 0;i < keys.size;i++ )
|
||||
if ( keys[ i ] == level.vision_set_transition_ent.vision_set )
|
||||
{
|
||||
index = i - 1;
|
||||
break;
|
||||
}
|
||||
if ( index < 0 )
|
||||
return;
|
||||
|
||||
setcurrentgroup( keys[index] );
|
||||
}
|
||||
|
||||
reset_cmds()
|
||||
{
|
||||
SetDevDvar( "scr_select_art_next", 0 );
|
||||
SetDevDvar( "scr_select_art_prev", 0 );
|
||||
}
|
||||
|
||||
setcurrentgroup( group )
|
||||
{
|
||||
keys = getarraykeys( level.vision_set_fog );
|
||||
|
||||
if ( level.currentgen )
|
||||
{
|
||||
group_cg = group + "_cg";
|
||||
index_cg = array_find( keys, group_cg );
|
||||
if ( IsDefined( index_cg ) )
|
||||
group = group_cg;
|
||||
}
|
||||
|
||||
level.spam_model_current_group = group;
|
||||
index = 0;
|
||||
div = int( level.spam_group_hudelems.size / 2 );
|
||||
for ( i = 0;i < keys.size;i++ )
|
||||
if ( keys[ i ] == group )
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
level.spam_group_hudelems[ div ] _settext( keys[ index ] );
|
||||
|
||||
for ( i = 1;i < level.spam_group_hudelems.size - div;i++ )
|
||||
{
|
||||
if ( index - i < 0 )
|
||||
{
|
||||
level.spam_group_hudelems[ div + i ] _settext( "." );
|
||||
continue;
|
||||
}
|
||||
level.spam_group_hudelems[ div + i ] _settext( keys[ index - i ] );
|
||||
}
|
||||
|
||||
for ( i = 1;i < level.spam_group_hudelems.size - div;i++ )
|
||||
{
|
||||
if ( index + i > keys.size - 1 )
|
||||
{
|
||||
// -- --
|
||||
level.spam_group_hudelems[ div - i ] _settext( "." );
|
||||
continue;
|
||||
}
|
||||
level.spam_group_hudelems[ div - i ] _settext( keys[ index + i ] );
|
||||
}
|
||||
|
||||
set_fog( keys[ index ], 0 ) ;
|
||||
}
|
||||
|
||||
get_fog( fogset )
|
||||
{
|
||||
if ( !isdefined( level.vision_set_fog ) )
|
||||
level.vision_set_fog = [];
|
||||
|
||||
ent = level.vision_set_fog[ fogset ];
|
||||
//assertex( IsDefined( ent ), "fog set: " + fogset + "does not exist, use create_fog( " + fogset + " ) in your level_fog.gsc." );
|
||||
return ent;
|
||||
}
|
||||
|
||||
|
||||
init_fog_transition()
|
||||
{
|
||||
if ( !IsDefined( level.fog_transition_ent ) )
|
||||
{
|
||||
level.fog_transition_ent = SpawnStruct();
|
||||
level.fog_transition_ent.fogset = "";
|
||||
level.fog_transition_ent.time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
playerInit()
|
||||
{
|
||||
last_vision_set = level.vision_set_transition_ent.vision_set;
|
||||
|
||||
//clear these so the vision set will happen.
|
||||
level.vision_set_transition_ent.vision_set = "";
|
||||
level.vision_set_transition_ent.time = "";
|
||||
|
||||
init_fog_transition();
|
||||
level.fog_transition_ent.fogset = "";
|
||||
level.fog_transition_ent.time = "";
|
||||
|
||||
setcurrentgroup( last_vision_set );
|
||||
}
|
||||
|
||||
button_down( btn, btn2 )
|
||||
{
|
||||
pressed = level.player ButtonPressed( btn );
|
||||
|
||||
if ( !pressed )
|
||||
{
|
||||
pressed = level.player ButtonPressed( btn2 );
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.buttons[ btn ] ) )
|
||||
{
|
||||
level.buttons[ btn ] = 0;
|
||||
}
|
||||
|
||||
// To Prevent Spam
|
||||
if ( GetTime() < level.buttons[ btn ] )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
level.buttons[ btn ] = GetTime() + 400;
|
||||
return pressed;
|
||||
}
|
||||
|
||||
dumpsettings()
|
||||
{
|
||||
/$
|
||||
if ( ( GetDvarInt( "scr_art_dump" ) == 0 ) )
|
||||
return false;
|
||||
|
||||
dump_art = GetDvarInt( "scr_art_dump" );
|
||||
|
||||
|
||||
SetDevDvar( "scr_art_dump", "0" );
|
||||
|
||||
|
||||
////////////////// _art.gsc
|
||||
artStartFogFileExport();
|
||||
fileprint_launcher( "// _createart generated. modify at your own risk. Changing values should be fine." );
|
||||
fileprint_launcher( "main()" );
|
||||
fileprint_launcher( "{" );
|
||||
|
||||
fileprint_launcher( "" );
|
||||
fileprint_launcher( "\tlevel.tweakfile = true;" );
|
||||
fileprint_launcher( " " );
|
||||
|
||||
fogpath = "maps\\createart\\" + GetDvar( "scr_art_visionfile" ) + "_fog";
|
||||
fileprint_launcher( "\tif (IsUsingHDR())" );
|
||||
fileprint_launcher( "\t\t" + fogpath + "_hdr::SetupFog( );" );
|
||||
fileprint_launcher( "\telse" );
|
||||
fileprint_launcher( "\t\t" + fogpath + "::SetupFog( );" );
|
||||
|
||||
fileprint_launcher( "\tVisionSetNaked( \"" + level.script + "\", 0 );" );
|
||||
|
||||
fileprint_launcher( "" );
|
||||
fileprint_launcher( "}" );
|
||||
artEndFogFileExport(); // This actually writes out the _art file
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
// MP doesn't write [level]_art.csv?
|
||||
|
||||
|
||||
////////////////// _fog.gsc
|
||||
art_print_fog(); // This is what really writes out the _fog file
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
////////////////// .vision
|
||||
// only print the currently selected vision file
|
||||
if ( dump_art )
|
||||
{
|
||||
if ( !common_scripts\_artCommon::print_vision( level.vision_set_transition_ent.vision_set ) )
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
IPrintLnBold( "ART DUMPED SUCCESSFULLY" );
|
||||
return true;
|
||||
$/
|
||||
}
|
||||
|
||||
//****** Pulled from _artcommon before integration
|
||||
artStartVisionFileExport()
|
||||
{
|
||||
fileprint_launcher_start_file();
|
||||
}
|
||||
|
||||
artEndVisionFileExport()
|
||||
{
|
||||
return fileprint_launcher_end_file( "\\share\\raw\\vision\\"+level.script+ ".vision", true );
|
||||
}
|
||||
|
||||
artStartFogFileExport()
|
||||
{
|
||||
fileprint_launcher_start_file();
|
||||
|
||||
}
|
||||
|
||||
artEndFogFileExport()
|
||||
{
|
||||
return fileprint_launcher_end_file( "\\share\\raw\\maps\\createart\\"+level.script+ "_art.gsc", true );
|
||||
}
|
||||
|
||||
artfxprintlnFog()
|
||||
{
|
||||
fileprint_launcher( "" );
|
||||
fileprint_launcher( "\t//* Fog section * " );
|
||||
fileprint_launcher( "" );
|
||||
|
||||
fileprint_launcher( "\tsetDevDvar( \"scr_fog_disable\"" + ", " + "\"" + GetDvarInt( "scr_fog_disable" ) + "\"" + " );" );
|
||||
|
||||
fileprint_launcher( "" );
|
||||
|
||||
fileprint_launcher( "\t/$" );
|
||||
if ( IsUsingHDR() )
|
||||
fileprint_launcher( "\tlevel._art_fog_setup = maps\\createart\\" + level.script + "_fog_hdr::main;" );
|
||||
else
|
||||
fileprint_launcher( "\tlevel._art_fog_setup = maps\\createart\\" + level.script + "_fog::main;" );
|
||||
fileprint_launcher( "\t$/" );
|
||||
}
|
||||
//****** End Pulled from _artcommon before integration
|
||||
|
||||
art_print_fog()
|
||||
{
|
||||
/$
|
||||
default_name = get_template_level();
|
||||
fileprint_launcher_start_file();
|
||||
fileprint_launcher( "// _createart generated. modify at your own risk. " );
|
||||
|
||||
fileprint_launcher( "main()" );
|
||||
fileprint_launcher( "{" );
|
||||
|
||||
common_scripts\_artCommon::print_fog_ents( true );
|
||||
|
||||
fileprint_launcher( "}" );
|
||||
|
||||
|
||||
fileprint_launcher( " " );
|
||||
fileprint_launcher( "setupfog()" );
|
||||
fileprint_launcher( "{" );
|
||||
|
||||
artfxprintlnFog();
|
||||
|
||||
fileprint_launcher( "}" );
|
||||
|
||||
if ( IsUsingHDR() )
|
||||
fileprint_launcher_end_file( "\\share\\raw\\maps\\createart\\" + default_name + "_fog_hdr.gsc", true );
|
||||
else
|
||||
fileprint_launcher_end_file( "\\share\\raw\\maps\\createart\\" + default_name + "_fog.gsc", true );
|
||||
$/
|
||||
}
|
||||
|
||||
create_light_set( name )
|
||||
{
|
||||
if ( !isdefined( level.light_set ) )
|
||||
level.light_set = [];
|
||||
ent = SpawnStruct();
|
||||
ent.name = name;
|
||||
|
||||
level.light_set[ name ] = ent;
|
||||
return ent;
|
||||
}
|
||||
|
871
raw/maps/mp/_audio.gsc
Normal file
871
raw/maps/mp/_audio.gsc
Normal file
@ -0,0 +1,871 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init_audio()
|
||||
{
|
||||
if ( !IsDefined( level.audio ) )
|
||||
{
|
||||
level.audio = SpawnStruct();
|
||||
}
|
||||
|
||||
init_reverb();
|
||||
init_whizby();
|
||||
|
||||
level.onPlayerConnectAudioInit = ::OnPlayerConnectAudioInit;
|
||||
}
|
||||
|
||||
OnPlayerConnectAudioInit()
|
||||
{
|
||||
self apply_reverb( "default" );
|
||||
// self apply_whizby(); <-- Does not work yet... Looks like it overflows the server.
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Reverb Section
|
||||
//---------------------------------------------------------
|
||||
init_reverb()
|
||||
{
|
||||
add_reverb( "default", "generic", 0.15, 0.9, 2 );
|
||||
}
|
||||
|
||||
add_reverb( name, type, wetlevel, drylevel, fadetime )
|
||||
{
|
||||
Assert( IsDefined( type ) );
|
||||
Assert( IsDefined( wetlevel ) );
|
||||
Assert( IsDefined( drylevel ) );
|
||||
|
||||
reverb = [];
|
||||
|
||||
is_roomtype_valid( type );
|
||||
|
||||
reverb[ "roomtype" ] = type;
|
||||
reverb[ "wetlevel" ] = wetlevel;
|
||||
reverb[ "drylevel" ] = drylevel;
|
||||
reverb[ "fadetime" ] = fadetime;
|
||||
|
||||
level.audio.reverb_settings[ name ] = reverb;
|
||||
}
|
||||
|
||||
is_roomtype_valid( type )
|
||||
{
|
||||
/#
|
||||
switch ( type )
|
||||
{
|
||||
case "generic":
|
||||
case "paddedcell":
|
||||
case "room":
|
||||
case "bathroom":
|
||||
case "livingroom":
|
||||
case "stoneroom":
|
||||
case "auditorium":
|
||||
case "concerthall":
|
||||
case "cave":
|
||||
case "arena":
|
||||
case "hangar":
|
||||
case "carpetedhallway":
|
||||
case "hallway":
|
||||
case "stonecorridor":
|
||||
case "alley":
|
||||
case "forest":
|
||||
case "city":
|
||||
case "mountains":
|
||||
case "quarry":
|
||||
case "plain":
|
||||
case "parkinglot":
|
||||
case "sewerpipe":
|
||||
case "underwater":
|
||||
case "drugged":
|
||||
case "dizzy":
|
||||
case "psychotic":
|
||||
return;
|
||||
default:
|
||||
AssertMsg( type + " is an Invalid Roomtype" );
|
||||
break;
|
||||
}
|
||||
#/
|
||||
}
|
||||
|
||||
apply_reverb( name )
|
||||
{
|
||||
if ( !IsDefined( level.audio.reverb_settings[ name ] ) )
|
||||
{
|
||||
reverb = level.audio.reverb_settings[ "default" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
reverb = level.audio.reverb_settings[ name ];
|
||||
}
|
||||
|
||||
self SetReverb( "snd_enveffectsprio_level", reverb[ "roomtype" ], reverb[ "drylevel" ], reverb[ "wetlevel" ], reverb[ "fadetime" ] );
|
||||
// self SetClientDvar( "cg_levelReverbRoomType", reverb[ "roomtype" ] );
|
||||
// self SetClientDvar( "cg_levelReverbDryLevel", reverb[ "drylevel" ] );
|
||||
// self SetClientDvar( "cg_levelReverbWetLevel", reverb[ "wetlevel" ] );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// whizBy Section
|
||||
//---------------------------------------------------------
|
||||
|
||||
init_whizby()
|
||||
{
|
||||
SetDevDvar( "snd_newwhizby", 1 );
|
||||
|
||||
// Default settings -- Call wrappers in your level to overwrite.
|
||||
level.audio.whizby_settings = [];
|
||||
set_whizby_radius( 15.0, 30.0, 50.0 );
|
||||
set_whizby_spread( 150.0, 250.0, 350.0 );
|
||||
}
|
||||
|
||||
set_whizby_radius( near, medium, far )
|
||||
{
|
||||
level.audio.whizby_settings[ "radius" ] = [ near, medium, far ];
|
||||
}
|
||||
|
||||
set_whizby_spread( near, medium, far )
|
||||
{
|
||||
level.audio.whizby_settings[ "spread" ] = [ near, medium, far ];
|
||||
}
|
||||
|
||||
apply_whizby()
|
||||
{
|
||||
settings = level.audio.whizby_settings;
|
||||
|
||||
spread = settings[ "spread" ];
|
||||
rad = settings[ "radius" ];
|
||||
|
||||
self SetWhizbySpreads( spread[ 0 ], spread[ 1 ], spread[ 2 ] );
|
||||
self SetWhizbyRadii( rad[ 0 ], rad[ 1 ], rad[ 2 ] );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_team_splash( ally_sfx, enemy_sfx )"
|
||||
"Summary: Plays a 2d splash sound on all players based on what team they are on."
|
||||
"CallOn: A Player"
|
||||
"OptionalArg: < ally_sfx, enemy_sfx > : soundaliases you want to play on players that are ally or enemies."
|
||||
"Example: warbird snd_play_team_splash( "my_team_captured_the_flag", "the_enemy_team_captured_the_flag" ) ;"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_play_team_splash( ally_sfx, enemy_sfx )
|
||||
{
|
||||
if(!isdefined( ally_sfx ))
|
||||
{
|
||||
ally_sfx = "null";
|
||||
}
|
||||
if(!isdefined( enemy_sfx ))
|
||||
{
|
||||
enemy_sfx = "null";
|
||||
}
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team != self.team )
|
||||
{
|
||||
// Play This Sound on all enemy players in a match.
|
||||
if( SoundExists( enemy_sfx ) )
|
||||
{
|
||||
player playlocalsound( enemy_sfx );
|
||||
}
|
||||
}
|
||||
else if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team == self.team )
|
||||
{
|
||||
// Play This Sound on all friendly players in a match.
|
||||
if( SoundExists( ally_sfx ) )
|
||||
{
|
||||
player playlocalsound( ally_sfx );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_on_notetrack_timer( alias, notetrack_frame, start_frame, _cleanup_time )"
|
||||
"Summary: Plays a sound after."
|
||||
"CallOn: An Entity"
|
||||
"Manditory Arg: <alias> : soundaliases you want to play."
|
||||
"Manditory Arg: <notetrack_frame> : The frame you want your sound to play on."
|
||||
"Manditory Arg: <start_frame> : The starting frame number of the animation you are watching."
|
||||
"OptionalArg: <_Cleanup_Time> : Time before the sound ent will be deleted (default is 8)."
|
||||
"Example: level.elevator snd_play_on_notetrack_timer( "elevator_crash", 414, 225, 15 );;"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_play_on_notetrack_timer( alias, notetrack_frame, start_frame, _cleanup_time )
|
||||
{
|
||||
//cleanup_time = 8;
|
||||
|
||||
//if( IsDefined( _cleanup_time ) )
|
||||
// cleanup_time = _cleanup_time;
|
||||
|
||||
//frame_dif = notetrack_frame - start_frame / 30;
|
||||
|
||||
//wait( frame_dif );
|
||||
|
||||
//snd_play_linked( alias, self, cleanup_time );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_on_notetrack( guy, animstring )"
|
||||
"Summary: Listens for and Plays a sound on a notetrack event."
|
||||
"CallOn: An Entity that is being animated on."
|
||||
"Manditory Arg: <aliases> : array of soundaliases you want to play."
|
||||
"Manditory Arg: <notetrack> : One vaild reference notetrack that exists in the animation, this must match the second arg of ScriptModelPlayAnimDeltaMotion used to call this anim.."
|
||||
"Example:
|
||||
***Anim - Just add valid notetrack in this case "laser_xform_up_sec1_start" ) self.lifter ScriptModelPlayAnimDeltaMotion( self.lifter.animUp, "laser_xform_up_sec1_start" );
|
||||
self.lifter thread snd_play_on_notetrack( notetracktoaliasarray, "laser_xform_up_sec1_start" );
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_play_on_notetrack( aliases, notetrack, _customfunction )
|
||||
{
|
||||
self endon( "stop_sequencing_notetracks" );
|
||||
self endon( "death" );
|
||||
self sndx_play_on_notetrack_internal( aliases, notetrack, _customfunction );
|
||||
}
|
||||
|
||||
sndx_play_on_notetrack_internal( aliases, notetrack, _customfunction )// _customfunction isn't even used. we should get rid of it.
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
self waittill( notetrack, note );
|
||||
|
||||
if ( isDefined( note ) && note != "end" )
|
||||
{
|
||||
if( isarray( aliases ))
|
||||
{
|
||||
alias = aliases[note];
|
||||
if ( IsDefined( alias ) )
|
||||
{
|
||||
self playsound( alias );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( notetrack == note )
|
||||
{
|
||||
self playsound( aliases );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: ScriptModelPlayAnimWithNotify( <guy>, <notifyName>, <alias> )"
|
||||
"Summary: Plays an animation and hooks into the notetrack, playing the alias when the notify is triggered."
|
||||
"CallOn: Ignored"
|
||||
"Example: ScriptModelPlayAnimWithNotify( level.thing, "sp_foo", "foo" );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
ScriptModelPlayAnimWithNotify( guy, animName, notifyName, alias, customlevelend, customguyend1, customguyend2 )
|
||||
{
|
||||
if ( IsDefined( customlevelend ) )
|
||||
{
|
||||
level endon( customlevelend );
|
||||
}
|
||||
|
||||
guy ScriptModelPlayAnimDeltaMotion( animName, notifyName );
|
||||
thread ScriptModelPlayAnimWithNotify_Notetracks( guy, notifyName, alias, customlevelend, customguyend1, customguyend2 );
|
||||
}
|
||||
|
||||
|
||||
ScriptModelPlayAnimWithNotify_Notetracks( guy, notifyName, alias, customlevelend, customguyend1, customguyend2 )
|
||||
{
|
||||
if ( IsDefined( customlevelend ) )
|
||||
{
|
||||
level endon( customlevelend );
|
||||
}
|
||||
|
||||
if ( IsDefined( customguyend1 ) )
|
||||
{
|
||||
guy endon( customguyend1 );
|
||||
}
|
||||
|
||||
if ( IsDefined( customguyend2 ) )
|
||||
{
|
||||
guy endon( customguyend2 );
|
||||
}
|
||||
|
||||
guy endon( "death" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
guy waittill( notifyName, note );
|
||||
|
||||
if ( isDefined( note ) && ( note == notifyName ) )
|
||||
{
|
||||
guy playSound( alias );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_veh_play_loops( <array> , <ent> )"
|
||||
"Summary: Plays up to 3 Loops on a vehicle by creating new sound ents and linking them to the functions self."
|
||||
"CallOn: An Entity"
|
||||
"OptionalArg: <loop_01, loop_02, loop_03, loop_04 > : soundaliases you want to link to this ent."
|
||||
"Example: warbird snd_veh_play_loops;"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
snd_veh_play_loops( loop_01, loop_02, loop_03 )
|
||||
{
|
||||
vehicle = self;
|
||||
loop_array = [ loop_01, loop_02, loop_03 ];
|
||||
|
||||
|
||||
ent_array[0] = spawn( "script_origin", vehicle.origin );
|
||||
ent_array[0] LinkToSynchronizedParent( vehicle );
|
||||
ent_array[0] playloopsound( loop_01 );
|
||||
|
||||
|
||||
ent_array[1] = spawn( "script_origin", vehicle.origin );
|
||||
ent_array[1] LinkToSynchronizedParent( vehicle );
|
||||
ent_array[1] playloopsound( loop_02 );
|
||||
|
||||
|
||||
ent_array[2] = spawn( "script_origin", vehicle.origin );
|
||||
ent_array[2] LinkToSynchronizedParent( vehicle );
|
||||
ent_array[2] playloopsound( loop_03 );
|
||||
|
||||
|
||||
vehicle waittill( "death" );
|
||||
|
||||
foreach( ent in ent_array )
|
||||
{
|
||||
if( isDefined( ent ) )
|
||||
{
|
||||
//vehicle_loop_ent setvolume(0, 0.05);
|
||||
wait(0.06);
|
||||
ent delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEPRECATED_aud_map(input, env_points)
|
||||
{
|
||||
// input is zero to one
|
||||
assert(IsDefined(input));
|
||||
assert(input >= 0.0 && input <= 1.0);
|
||||
assert(IsDefined(env_points));
|
||||
|
||||
/#
|
||||
// this ensures that the given envelope is in order, otherwise we'd have to perform a sorting function.
|
||||
//audx_validate_env_array(env_points);
|
||||
#/
|
||||
output = 0.0;
|
||||
num_points = env_points.size;
|
||||
|
||||
// find the x-values which are relevant for the input
|
||||
prev_point = env_points[0]; // grab the first point
|
||||
for (i = 1; i < env_points.size; i++)
|
||||
{
|
||||
next_point = env_points[i];
|
||||
if (input >= prev_point[0] && input <= next_point[0])
|
||||
{
|
||||
prev_x = prev_point[0];
|
||||
next_x = next_point[0];
|
||||
prev_y = prev_point[1];
|
||||
next_y = next_point[1];
|
||||
x_fract = (input - prev_x) / (next_x - prev_x);
|
||||
output = prev_y + x_fract * (next_y - prev_y);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_point = next_point;
|
||||
}
|
||||
}
|
||||
|
||||
assert(output >= 0.0 && output <= 1.0);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_loop_in_space( <alias_name> , <org>, <stop_loop_notify>, <fadeout_time_> )"
|
||||
"Summary: Plays a loopsound in space with a stop loop notify string. "
|
||||
"Module: Sound"
|
||||
"CallOn: A Thread"
|
||||
"MandatoryArg: <alias_name>: name of the alias"
|
||||
"MandatoryArg: <org>: Vector"
|
||||
"MandatoryArg: <stop_loop_notify>: Notify string used to stop the sound."
|
||||
"OptionalArg: <fadeout_time_>: Changes the fadeout time of the sound once the stop loop notify is called."
|
||||
"Example: "snd_play_loop_in_space( "best_fire_loop_ever" , ( 100, 1203, 10 ), "stop_best_fire_loop_ever", 0.5 )"
|
||||
"SPMP: Multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
snd_play_loop_in_space( alias_name, org, stop_loop_notify, fadeout_time_ )
|
||||
{
|
||||
// Default Fadeout Time.
|
||||
fadeout_time = 0.2;
|
||||
|
||||
if(isDefined( fadeout_time_ ) )
|
||||
fadeout_time = fadeout_time_;
|
||||
|
||||
snd_ent = spawn( "script_origin", org );
|
||||
snd_ent playloopsound( alias_name );
|
||||
|
||||
thread sndx_play_loop_in_space_internal( snd_ent, stop_loop_notify, fadeout_time );
|
||||
|
||||
return snd_ent;
|
||||
|
||||
}
|
||||
|
||||
sndx_play_loop_in_space_internal( snd_ent, stop_loop_notify, fadeout_time )
|
||||
{
|
||||
|
||||
level waittill( stop_loop_notify );
|
||||
|
||||
if(IsDefined( snd_ent ) )
|
||||
{
|
||||
snd_ent scalevolume( 0, fadeout_time );
|
||||
wait( fadeout_time + 0.05 );
|
||||
snd_ent delete();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_script_timer( <update_rate> )"
|
||||
"Summary: prints timestamps to easily time events in script. "
|
||||
"Module: Sound"
|
||||
"MandatoryArg: <update_rate>: how fast the prints happen.
|
||||
"Example: "snd_script_timer( 0.1 )"
|
||||
"SPMP: Multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_script_timer( speed )
|
||||
{
|
||||
level.timer_number = 0;
|
||||
|
||||
if(!IsDefined( speed ))
|
||||
{
|
||||
// Default Speed set to 0.1
|
||||
speed = 0.1;
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
iprintln( level.timer_number );
|
||||
wait( speed );
|
||||
level.timer_number = level.timer_number + speed;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_in_space( <alias_name> , <org>, <_cleanuptime> )"
|
||||
"Summary: Plays a sound in space, returns an ent and cleans it up when done. "
|
||||
"Module: Sound"
|
||||
"CallOn: A Thread"
|
||||
"MandatoryArg: <alias_name>: name of the alias"
|
||||
"MandatoryArg: <org>: Vector"
|
||||
"MandatoryArg: <_cleanuptime>: Time before ents are cleaned up."
|
||||
"Example: "snd_play_in_space( "playasoundhereomg" , ( 100, 1203, 10 ) )"
|
||||
"SPMP: Multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_play_in_space( alias_name, org, _cleanup_time, _fadeout_time )
|
||||
{
|
||||
cleanup_time = 9;
|
||||
fadeout_time = 0.75;
|
||||
|
||||
snd_ent = spawn( "script_origin", org );
|
||||
snd_ent playsound( alias_name );
|
||||
snd_ent thread sndx_play_in_space_internal( cleanup_time, fadeout_time );
|
||||
return snd_ent;
|
||||
}
|
||||
|
||||
sndx_play_in_space_internal( _cleanup_time, _fadeout_time )
|
||||
{
|
||||
cleanup_time = 9;
|
||||
fadeout_time = 0.05;
|
||||
snd_ent = self;
|
||||
|
||||
if( IsDefined( _cleanup_time ) )
|
||||
cleanup_time = _cleanup_time;
|
||||
|
||||
if( IsDefined( _fadeout_time ) )
|
||||
fadeout_time = _fadeout_time;
|
||||
|
||||
wait( cleanup_time );
|
||||
|
||||
if(IsDefined( snd_ent ) )
|
||||
{
|
||||
snd_ent scalevolume( 0, fadeout_time );
|
||||
wait( fadeout_time + 0.05 );
|
||||
|
||||
if(IsDefined( snd_ent ))
|
||||
snd_ent delete();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_in_space_delayed( <alias_name> , <org>, <delay_time>, <_cleanuptime> )"
|
||||
"Summary: Plays a sound in space, returns an ent and cleans it up when done. "
|
||||
"Module: Sound"
|
||||
"CallOn: A Thread"
|
||||
"MandatoryArg: <alias_name>: name of the alias"
|
||||
"MandatoryArg: <org>: Vector"
|
||||
"MandatoryArg: <_cleanuptime>: Time before ents are cleaned up."
|
||||
"MandatoryArg: <delay_time>: Time before sound starts."
|
||||
"Example: "snd_play_in_space( "playasoundhereomg" , ( 100, 1203, 10 ) )"
|
||||
"SPMP: Multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_play_in_space_delayed( alias_name, org, delay_time, _cleanup_time, _fadeout_time )
|
||||
{
|
||||
cleanup_time = 9;
|
||||
fadeout_time = 0.75;
|
||||
|
||||
snd_ent = spawn( "script_origin", org );
|
||||
snd_ent thread sndx_play_in_space_delayed_internal( alias_name, delay_time, _cleanup_time, _fadeout_time );
|
||||
return snd_ent;
|
||||
}
|
||||
|
||||
sndx_play_in_space_delayed_internal( alias_name, delay_time, _cleanup_time, _fadeout_time )
|
||||
{
|
||||
wait( delay_time );
|
||||
cleanup_time = 9;
|
||||
fadeout_time = 0.05;
|
||||
snd_ent = self;
|
||||
|
||||
snd_ent playsound( alias_name );
|
||||
|
||||
if( IsDefined( _cleanup_time ) )
|
||||
cleanup_time = _cleanup_time;
|
||||
|
||||
if( IsDefined( _fadeout_time ) )
|
||||
fadeout_time = _fadeout_time;
|
||||
|
||||
wait( cleanup_time );
|
||||
|
||||
if(IsDefined( snd_ent ) )
|
||||
{
|
||||
snd_ent scalevolume( 0, fadeout_time );
|
||||
wait( fadeout_time + 0.05 );
|
||||
|
||||
if(IsDefined( snd_ent ))
|
||||
snd_ent delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_linked( <alias_name> , <ent>, <_cleanuptime>, <_fadeouttime> )"
|
||||
"Summary: Plays a sound attached on an ent, returns ent and cleans it up when done. "
|
||||
"Module: Sound"
|
||||
"CallOn: A Thread"
|
||||
"MandatoryArg: <alias_name>: name of the alias"
|
||||
"MandatoryArg: <ent>: Ent you wish to attach sound to"
|
||||
"OptionalArg: <_cleanuptime>: Time before ents are cleaned up."
|
||||
"OptionalArg: <_fadeouttime>: Fade time before the ent is deleted (after cleanuptime is complete."
|
||||
"Example: "snd_play_in_space( "play_a_sound_on_a_thing" , ent )"
|
||||
"SPMP: Multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_play_linked( alias_name, ent, _cleanup_time, _fadeout_time )
|
||||
{
|
||||
snd_ent = spawn( "script_origin", ent.origin );
|
||||
snd_ent linkto( ent );
|
||||
snd_ent thread sndx_play_linked_internal( alias_name, ent, _cleanup_time, _fadeout_time );
|
||||
return snd_ent;
|
||||
|
||||
}
|
||||
|
||||
sndx_play_linked_internal( alias_name, ent, _cleanup_time, _fadeout_time )
|
||||
{
|
||||
cleanup_time = 9;
|
||||
fadeout_time = 0.05;
|
||||
snd_ent = self;
|
||||
|
||||
snd_ent playsound( alias_name );
|
||||
|
||||
if( IsDefined( _cleanup_time ) )
|
||||
cleanup_time = _cleanup_time;
|
||||
|
||||
if( IsDefined( _fadeout_time ) )
|
||||
fadeout_time = _fadeout_time;
|
||||
|
||||
wait( cleanup_time );
|
||||
|
||||
if(IsDefined( snd_ent ) )
|
||||
{
|
||||
snd_ent scalevolume( 0, fadeout_time );
|
||||
wait( fadeout_time + 0.05 );
|
||||
snd_ent delete();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: snd_play_linked_loop( <alias_name> , <ent>, <_cleanuptime>, <_fadeouttime> )"
|
||||
"Summary: Plays a sound attached on an ent, returns ent and cleans it up when done. "
|
||||
"Module: Sound"
|
||||
"CallOn: A Thread"
|
||||
"MandatoryArg: <alias_name>: name of the alias"
|
||||
"MandatoryArg: <ent>: Ent you wish to attach sound to"
|
||||
"OptionalArg: <_fadeouttime>: Fade time before the ent is deleted."
|
||||
"Example: snd_play_in_space( "play_a_sound_on_a_thing" , ent )
|
||||
"SPMP: Multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
snd_play_linked_loop( alias_name, ent, _fadeout_time )
|
||||
{
|
||||
snd_ent = spawn( "script_origin", ent.origin );
|
||||
snd_ent linkto( ent );
|
||||
snd_ent thread sndx_play_linked_loop_internal( alias_name, ent, _fadeout_time );
|
||||
return snd_ent;
|
||||
|
||||
}
|
||||
|
||||
sndx_play_linked_loop_internal( alias_name, ent, _fadeout_time )
|
||||
{
|
||||
fadeout_time = 0.05;
|
||||
snd_ent = self;
|
||||
|
||||
snd_ent playloopsound( alias_name );
|
||||
|
||||
if( IsDefined( _fadeout_time ) )
|
||||
fadeout_time = _fadeout_time;
|
||||
|
||||
ent waittill( "death" );
|
||||
|
||||
if(IsDefined( snd_ent ) )
|
||||
{
|
||||
snd_ent scalevolume( 0, fadeout_time );
|
||||
wait( fadeout_time + 0.05 );
|
||||
snd_ent delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
///ScriptDocBegin
|
||||
"Name: aud_print_3d_on_ent(<msg> , <_size>, <_text_color> )"
|
||||
"Summary: Create a print msg in 3d space that follows the entity this function is called on."
|
||||
"Module: Audio"
|
||||
"CallOn: The entity you wish to tag your print msg to."
|
||||
"MandatoryArg: <msg> : Your print msg."
|
||||
"OptionalArg": <_size> : Text size.
|
||||
"OptionalArg": <_text_color> : Text color.
|
||||
"OptionalArg": <_msg_callback> : Function pointer that returns a string which will be concatenated to <msg>. _msg_callback is called on self.
|
||||
"OptionalArg": <_msg_callback> : Duration in seconds that the 3D message should be printed on self.
|
||||
"SPMP: multiplayer"
|
||||
///ScriptDocEnd
|
||||
*/
|
||||
aud_print_3d_on_ent(msg, _size, _text_color, _msg_callback, durration_)
|
||||
{
|
||||
if(IsDefined(self))
|
||||
{
|
||||
|
||||
white = (1, 1, 1); //Default color if no arg is given.
|
||||
red = (1, 0, 0);
|
||||
green = (0, 1, 0);
|
||||
blue = (0, 1, 1);
|
||||
|
||||
size = 5;
|
||||
text_color = white;
|
||||
|
||||
if(IsDefined(_size))
|
||||
{
|
||||
size = _size;
|
||||
}
|
||||
|
||||
if(IsDefined( _text_color))
|
||||
{
|
||||
text_color = _text_color;
|
||||
|
||||
switch( text_color )
|
||||
{
|
||||
case "red":
|
||||
{
|
||||
text_color = red;
|
||||
}
|
||||
break;
|
||||
case "white":
|
||||
{
|
||||
text_color = white;
|
||||
}
|
||||
break;
|
||||
case "blue":
|
||||
{
|
||||
text_color = blue;
|
||||
}
|
||||
break;
|
||||
case "green":
|
||||
{
|
||||
text_color = green;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
text_color = white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsDefined(durration_))
|
||||
{
|
||||
self thread audx_print_3d_timer(durration_);
|
||||
}
|
||||
|
||||
self endon("death");
|
||||
self endon("aud_stop_3D_print");
|
||||
|
||||
while(IsDefined(self))
|
||||
{
|
||||
full_msg = msg;
|
||||
if (IsDefined(_msg_callback))
|
||||
{
|
||||
full_msg = full_msg + self [[_msg_callback]]();
|
||||
}
|
||||
Print3d(self.origin, full_msg, text_color, 1, size, 1);
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audx_print_3d_timer( durration_ )
|
||||
{
|
||||
self endon("death");
|
||||
assert(IsDefined(durration_));
|
||||
wait(durration_);
|
||||
if (IsDefined(self))
|
||||
{
|
||||
self notify("aud_stop_3D_print");
|
||||
}
|
||||
}
|
||||
|
||||
// NOTES
|
||||
// Checks to see if game mode is team based.
|
||||
//level.teamBased
|
||||
//self endon( "disconnect" );
|
||||
//level endon( "game_ended" );
|
||||
|
||||
|
||||
|
||||
snd_vehicle_mp()
|
||||
{
|
||||
// vehicle = self;
|
||||
//
|
||||
// //Setup Default Envelopes.
|
||||
// hover_env = [ [0,1],[1,0.25] ];
|
||||
// move_env = [ [0,0.3],[1,1] ];
|
||||
//
|
||||
// if( isdefined( _hover_env ) )
|
||||
// hover_env = _hover_env;
|
||||
//
|
||||
// if( isdefined( _move_env ) )
|
||||
// move_env = _move_env;
|
||||
//
|
||||
// // Create Ents and link them to the vehicle.
|
||||
// ent_array[ "hover_lp" ] = spawn( "script_origin", vehicle.origin );
|
||||
// ent_array[ "hover_lp" ] LinkToSynchronizedParent( vehicle );
|
||||
// ent_array[ "hover_lp" ] playloopsound( close_lp );
|
||||
//
|
||||
// ent_array[ "move_lp" ] = spawn( "script_origin", vehicle.origin );
|
||||
// ent_array[ "move_lp" ] LinkToSynchronizedParent( vehicle );
|
||||
// ent_array[ "move_lp" ] playloopsound( move_lp );
|
||||
//
|
||||
// ent_array[ "dist_lp" ] = spawn( "script_origin", vehicle.origin );
|
||||
// ent_array[ "dist_lp" ] LinkToSynchronizedParent( vehicle );
|
||||
// ent_array[ "dist_lp" ] playloopsound( dist_lp );
|
||||
//
|
||||
// velocity = 0.0;
|
||||
//
|
||||
// //Scale Volumes Based on Inputs
|
||||
// while( Isdefined( vehicle ))
|
||||
// {
|
||||
//
|
||||
// //if is player controlled....
|
||||
// new_velocity = vehicle Vehicle_GetSpeed();
|
||||
// if( new_velocity <= 0 )
|
||||
// {
|
||||
// player_velocity = vehicle GetEntityVelocity();
|
||||
// if (isdefined(player_velocity))
|
||||
// {
|
||||
// new_velocity = Length2D(player_velocity);
|
||||
// }
|
||||
// }
|
||||
// new_velocity = clamp(new_velocity, 0.0, 10.0) / 10.0;
|
||||
// if ( new_velocity > velocity)
|
||||
// {
|
||||
// //velocity = (new_velocity + velocity) / 2.0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// velocity = new_velocity;
|
||||
// }
|
||||
//
|
||||
// hover_vol = DEPRECATED_aud_map( velocity, hover_env );
|
||||
// ent_array[ "hover_lp" ] scalevolume( hover_vol, 0.05 );
|
||||
//
|
||||
// move_vol = DEPRECATED_aud_map( velocity, move_env );
|
||||
// ent_array[ "move_lp" ] scalevolume( move_vol, 0.05 );
|
||||
// wait(0.05);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// //vehicle waittill( "death" );
|
||||
//
|
||||
// foreach( ent in ent_array )
|
||||
// {
|
||||
// if( isDefined( ent ) )
|
||||
// {
|
||||
// ent setvolume(0, 1);
|
||||
// wait( 1 );
|
||||
// ent delete();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
470
raw/maps/mp/_awards.gsc
Normal file
470
raw/maps/mp/_awards.gsc
Normal file
@ -0,0 +1,470 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
/#
|
||||
CONST_PRINT_AWARDS = false;
|
||||
#/
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
initAwards();
|
||||
|
||||
level thread onPlayerConnect();
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
if ( !isDefined( player.pers["stats"] ) )
|
||||
player.pers["stats"] = [];
|
||||
|
||||
player.stats = player.pers["stats"];
|
||||
|
||||
// initialize player stats
|
||||
if( !player.stats.size )
|
||||
{
|
||||
foreach ( ref, award in level.awards )
|
||||
{
|
||||
player initPlayerStat( ref, level.awards[ ref ].defaultvalue );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
initAwards()
|
||||
{
|
||||
// medals
|
||||
initStatAward( "headshots", 0, ::highestWins );
|
||||
initStatAward( "multikill", 0, ::highestWins );
|
||||
initStatAward( "avengekills", 0, ::highestWins );
|
||||
initStatAward( "comebacks", 0, ::highestWins );
|
||||
initStatAward( "rescues", 0, ::highestWins );
|
||||
initStatAward( "longshots", 0, ::highestWins );
|
||||
initStatAward( "revengekills", 0, ::highestWins );
|
||||
initStatAward( "bulletpenkills", 0, ::highestWins );
|
||||
initStatAward( "throwback_kill", 0, ::highestWins );
|
||||
initStatAward( "firstblood", 0, ::highestWins );
|
||||
initStatAward( "posthumous", 0, ::highestWins );
|
||||
initStatAward( "assistedsuicide", 0, ::highestWins );
|
||||
initStatAward( "buzzkill", 0, ::highestWins );
|
||||
initStatAward( "oneshotkill", 0, ::highestWins );
|
||||
initStatAward( "air_to_air_kill", 0, ::highestWins );
|
||||
initStatAward( "air_to_ground_kill", 0, ::highestWins );
|
||||
initStatAward( "ground_to_air_kill", 0, ::highestWins );
|
||||
initStatAward( "doublekill", 0, ::highestWins );
|
||||
initStatAward( "triplekill", 0, ::highestWins );
|
||||
initStatAward( "fourkill", 0, ::highestWins );
|
||||
initStatAward( "fivekill", 0, ::highestWins );
|
||||
initStatAward( "sixkill", 0, ::highestWins );
|
||||
initStatAward( "sevenkill", 0, ::highestWins );
|
||||
initStatAward( "eightkill", 0, ::highestWins );
|
||||
initStatAward( "hijacker", 0, ::highestWins );
|
||||
initStatAward( "backstab", 0, ::highestWins );
|
||||
initStatAward( "5killstreak", 0, ::highestWins );
|
||||
initStatAward( "10killstreak", 0, ::highestWins );
|
||||
initStatAward( "15killstreak", 0, ::highestWins );
|
||||
initStatAward( "20killstreak", 0, ::highestWins );
|
||||
initStatAward( "25killstreak", 0, ::highestWins );
|
||||
initStatAward( "30killstreak", 0, ::highestWins );
|
||||
initStatAward( "30pluskillstreak", 0, ::highestWins );
|
||||
initStatAward( "pointblank", 0, ::highestWins );
|
||||
initStatAward( "firstplacekill", 0, ::highestWins );
|
||||
initStatAward( "boostslamkill", 0, ::highestWins );
|
||||
initStatAward( "assault", 0, ::highestWins );
|
||||
initStatAward( "defends", 0, ::highestWins );
|
||||
initStatAward( "exo_knife_kill", 0, ::highestWins );
|
||||
initStatAward( "exo_knife_recall_kill", 0, ::highestWins );
|
||||
initStatAward( "near_death_kill", 0, ::highestWins );
|
||||
initStatAward( "slide_kill", 0, ::highestWins );
|
||||
initStatAward( "flash_kill", 0, ::highestWins );
|
||||
initStatAward( "riot_kill", 0, ::highestWins );
|
||||
initStatAward( "melee_air_to_air", 0, ::highestWins );
|
||||
initStatAward( "assist_riot_shield", 0, ::highestWins );
|
||||
initStatAward( "semtex_stick", 0, ::highestWins );
|
||||
initStatAward( "stuck_with_explosive", 0, ::highestWins );
|
||||
initStatAward( "crossbow_stick", 0, ::highestWins );
|
||||
initStatAward( "multiKillOneBullet", 0, ::highestWins );
|
||||
initStatAward( "think_fast", 0, ::highestWins );
|
||||
initStatAward( "take_and_kill", 0, ::highestWins );
|
||||
initStatAward( "four_play", 0, ::highestWins );
|
||||
initStatAward( "sharepackage", 0, ::highestWins );
|
||||
initStatAward( "map_killstreak", 0, ::highestWins );
|
||||
initStatAward( "killstreak_tag", 0, ::highestWins );
|
||||
initStatAward( "killstreak_join", 0, ::highestWins );
|
||||
|
||||
// not medals but need for comabt record data
|
||||
initStatAward( "kills", 0, ::highestWins );
|
||||
initStatAward( "longestkillstreak", 0, ::highestWins );
|
||||
initStatAward( "knifekills", 0, ::highestWins );
|
||||
initStatAward( "kdratio", 0, ::highestWins );
|
||||
initStatAward( "deaths", 0, ::lowestWithHalfPlayedTime );
|
||||
initStatAward( "assists", 0, ::highestWins );
|
||||
initStatAward( "totalGameScore", 0, ::highestWins );
|
||||
initStatAward( "scorePerMinute", 0, ::highestWins );
|
||||
initStatAward( "mostScorePerLife", 0, ::highestWins );
|
||||
initStatAward( "killStreaksUsed", 0, ::highestWins );
|
||||
|
||||
// gun game medals
|
||||
initStatAward( "humiliation", 0, ::highestWins );
|
||||
initStatAward( "regicide", 0, ::highestWins );
|
||||
initStatAward( "gunslinger", 0, ::highestWins );
|
||||
initStatAward( "dejavu", 0, ::highestWins );
|
||||
initStatAward( "levelup", 0, ::highestWins );
|
||||
|
||||
// infected medals
|
||||
initStatAward( "omegaman", 0, ::highestWins );
|
||||
initStatAward( "plague", 0, ::highestWins );
|
||||
initStatAward( "patientzero", 0, ::highestWins );
|
||||
initStatAward( "careless", 0, ::highestWins );
|
||||
initStatAward( "survivor", 0, ::highestWins );
|
||||
initStatAward( "contagious", 0, ::highestWins );
|
||||
|
||||
// capture the flag medals
|
||||
initStatAward( "flagscaptured", 0, ::highestWins );
|
||||
initStatAward( "flagsreturned", 0, ::highestWins);
|
||||
initStatAward( "flagcarrierkills", 0, ::highestWins );
|
||||
initStatAward( "flagscarried", 0, ::highestWins );
|
||||
initStatAward( "killsasflagcarrier", 0, ::highestWins );
|
||||
|
||||
// domination medals
|
||||
initStatAward( "pointscaptured", 0, ::highestWins );
|
||||
initStatAward( "kill_while_capture", 0, ::highestWins );
|
||||
initStatAward( "opening_move", 0, ::highestWins );
|
||||
|
||||
// hard point medals
|
||||
initStatAward( "hp_secure", 0, ::highestWins );
|
||||
|
||||
// SD/SR medals
|
||||
initStatAward( "targetsdestroyed", 0, ::highestWins );
|
||||
initStatAward( "bombsplanted", 0, ::highestWins );
|
||||
initStatAward( "bombsdefused", 0, ::highestWins );
|
||||
initStatAward( "ninja_defuse", 0, ::highestWins );
|
||||
initStatAward( "last_man_defuse", 0, ::highestWins );
|
||||
initStatAward( "elimination", 0, ::highestWins );
|
||||
initStatAward( "last_man_standing", 0, ::highestWins );
|
||||
initStatAward( "sr_tag_elimination", 0, ::highestWins );
|
||||
initStatAward( "sr_tag_revive", 0, ::highestWins );
|
||||
|
||||
// kill confirmed medals
|
||||
initStatAward( "killsconfirmed", 0, ::highestWins );
|
||||
initStatAward( "killsdenied", 0, ::highestWins );
|
||||
initStatAward( "kill_denied_retrieved", 0, ::highestWins );
|
||||
initStatAward( "tag_collector", 0, ::highestWins );
|
||||
|
||||
// ball medals
|
||||
initStatAward( "touchdown", 0, ::highestWins );
|
||||
initStatAward( "fieldgoal", 0, ::highestWins );
|
||||
initStatAward( "interception", 0, ::highestWins );
|
||||
initStatAward( "kill_with_ball", 0, ::highestWins );
|
||||
initStatAward( "ball_score_assist", 0, ::highestWins );
|
||||
initStatAward( "pass_kill_pickup", 0, ::highestWins );
|
||||
initStatAward( "killedBallCarrier", 0, ::highestWins );
|
||||
|
||||
// killstreak destroyed medals
|
||||
initStatAward( "uav_destroyed", 0, ::highestWins );
|
||||
initStatAward( "warbird_destroyed", 0, ::highestWins );
|
||||
initStatAward( "paladin_destroyed", 0, ::highestWins );
|
||||
initStatAward( "vulcan_destroyed", 0, ::highestWins );
|
||||
initStatAward( "goliath_destroyed", 0, ::highestWins );
|
||||
initStatAward( "missile_strike_destroyed", 0, ::highestWins );
|
||||
initStatAward( "sentry_gun_destroyed", 0, ::highestWins );
|
||||
initStatAward( "strafing_run_destroyed", 0, ::highestWins );
|
||||
initStatAward( "assault_drone_destroyed", 0, ::highestWins );
|
||||
initStatAward( "recon_drone_destroyed", 0, ::highestWins );
|
||||
initStatAward( "map_killstreak_destroyed", 0, ::highestWins );
|
||||
initStatAward( "assist_killstreak_destroyed", 0, ::highestWins );
|
||||
|
||||
// killstreak kill medals
|
||||
initStatAward( "warbird_kill", 0, ::highestWins );
|
||||
initStatAward( "paladin_kill", 0, ::highestWins );
|
||||
initStatAward( "vulcan_kill", 0, ::highestWins );
|
||||
initStatAward( "goliath_kill", 0, ::highestWins );
|
||||
initStatAward( "airdrop_kill", 0, ::highestWins );
|
||||
initStatAward( "airdrop_trap_kill", 0, ::highestWins );
|
||||
initStatAward( "missile_strike_kill", 0, ::highestWins );
|
||||
initStatAward( "sentry_gun_kill", 0, ::highestWins );
|
||||
initStatAward( "strafing_run_kill", 0, ::highestWins );
|
||||
initStatAward( "assault_drone_kill", 0, ::highestWins );
|
||||
initStatAward( "map_killstreak_kill", 0, ::highestWins );
|
||||
initStatAward( "coop_killstreak_kill", 0, ::highestWins );
|
||||
|
||||
// killstreak earned medals
|
||||
initStatAward( "uav_earned", 0, ::highestWins );
|
||||
initStatAward( "warbird_earned", 0, ::highestWins );
|
||||
initStatAward( "orbitalsupport_earned", 0, ::highestWins );
|
||||
initStatAward( "orbital_strike_laser_earned", 0, ::highestWins );
|
||||
initStatAward( "orbital_carepackage_earned", 0, ::highestWins );
|
||||
initStatAward( "heavy_exosuit_earned", 0, ::highestWins );
|
||||
initStatAward( "missile_strike_earned", 0, ::highestWins );
|
||||
initStatAward( "remote_mg_sentry_turret_earned",0, ::highestWins );
|
||||
initStatAward( "strafing_run_airstrike_earned", 0, ::highestWins );
|
||||
initStatAward( "assault_ugv_earned", 0, ::highestWins );
|
||||
initStatAward( "recon_ugv_earned", 0, ::highestWins );
|
||||
initStatAward( "emp_earned", 0, ::highestWins );
|
||||
|
||||
// this needs to be the last stat in the list
|
||||
initStatAward( "numMatchesRecorded", 0, ::highestWins );
|
||||
}
|
||||
|
||||
|
||||
initStatAward( ref, defaultvalue, process, var1, var2 )
|
||||
{
|
||||
assert( isDefined( ref ) );
|
||||
|
||||
level.awards[ ref ] = spawnStruct();
|
||||
level.awards[ ref ].defaultvalue = defaultvalue;
|
||||
|
||||
if( isDefined( process ) )
|
||||
level.awards[ ref ].process = process;
|
||||
|
||||
if( isDefined( var1 ) )
|
||||
level.awards[ ref ].var1 = var1;
|
||||
|
||||
if( isDefined( var2 ) )
|
||||
level.awards[ ref ].var2 = var2;
|
||||
}
|
||||
|
||||
|
||||
setPersonalBestIfGreater( ref )
|
||||
{
|
||||
recordValue = self getCommonPlayerData( "bests", ref );
|
||||
playerValue = self getPlayerStat( ref );
|
||||
playerValue = getFormattedValue( ref, playerValue );
|
||||
|
||||
if ( recordValue == 0 || ( playervalue > recordValue ) )
|
||||
{
|
||||
self setCommonPlayerData( "bests", ref, playerValue );
|
||||
}
|
||||
}
|
||||
|
||||
setPersonalBestIfLower( ref )
|
||||
{
|
||||
recordvalue = self getCommonPlayerData( "bests", ref );
|
||||
playervalue = self getPlayerStat( ref );
|
||||
playerValue = getFormattedValue( ref, playerValue );
|
||||
|
||||
if ( recordValue == 0 || ( playervalue < recordvalue ) )
|
||||
{
|
||||
self setCommonPlayerData( "bests", ref, playerValue );
|
||||
}
|
||||
}
|
||||
|
||||
calculateKD( player )
|
||||
{
|
||||
kills = player getPlayerStat( "kills" );
|
||||
deaths = player getPlayerStat( "deaths" );
|
||||
|
||||
if ( deaths == 0 )
|
||||
deaths = 1;
|
||||
|
||||
player setPlayerStat( "kdratio", ( kills / deaths ) );
|
||||
}
|
||||
|
||||
getTotalScore( player )
|
||||
{
|
||||
totalScore = player.score;
|
||||
|
||||
if( !level.teamBased )
|
||||
totalScore = player.extrascore0;
|
||||
|
||||
return totalScore;
|
||||
}
|
||||
|
||||
calculateSPM( player )
|
||||
{
|
||||
if( player.timePlayed["total"] < 1 )
|
||||
return;
|
||||
|
||||
totalScore = getTotalScore( player );
|
||||
secondsPlayed = player.timePlayed["total"];
|
||||
scorePerMinute = totalScore / (secondsPlayed / 60 );
|
||||
|
||||
player setPlayerStat( "totalGameScore", totalScore );
|
||||
player setPlayerStat( "scorePerMinute", scorePerMinute );
|
||||
}
|
||||
|
||||
assignAwards()
|
||||
{
|
||||
// Special case handling of awards which get stat values updated at the end of a game
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
// dont assign awards if not ranked match
|
||||
if ( !player rankingEnabled() )
|
||||
return;
|
||||
|
||||
player incPlayerStat( "numMatchesRecorded", 1 );
|
||||
|
||||
calculateKD( player );
|
||||
calculateSPM( player );
|
||||
}
|
||||
|
||||
// process end of match stats
|
||||
foreach ( ref, award in level.awards )
|
||||
{
|
||||
if ( !isDefined( level.awards[ ref ].process ) )
|
||||
continue;
|
||||
|
||||
process = level.awards[ ref ].process;
|
||||
var1 = level.awards[ ref ].var1;
|
||||
var2 = level.awards[ ref ].var2;
|
||||
|
||||
if ( isDefined( var1 ) && isDefined( var2 ) )
|
||||
[[ process ]]( ref, var1, var2 );
|
||||
else if ( isDefined( var1 ) )
|
||||
[[ process ]]( ref, var1 );
|
||||
else
|
||||
[[ process ]]( ref );
|
||||
}
|
||||
|
||||
/#
|
||||
if( CONST_PRINT_AWARDS )
|
||||
{
|
||||
printAwards();
|
||||
}
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
printAwards()
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if( IsAI(player) )
|
||||
continue;
|
||||
|
||||
awardNames = GetArrayKeys( level.awards );
|
||||
|
||||
for ( i = 0; i < awardNames.size; i++ )
|
||||
{
|
||||
value = player getCommonPlayerData( "round", "awards", awardNames[i] );
|
||||
|
||||
println( "Awards: [", player.name, "][", awardNames[i], "] = ", value );
|
||||
}
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
giveAward( ref )
|
||||
{
|
||||
value = self getPlayerStat( ref );
|
||||
value = getFormattedValue( ref, value );
|
||||
|
||||
// set awards values for the round
|
||||
self setCommonPlayerData( "round", "awards", ref, value );
|
||||
|
||||
if ( practiceRoundGame() )
|
||||
return;
|
||||
|
||||
// set the award values for lifetime total
|
||||
if( shouldAverageTotal( ref ) )
|
||||
{
|
||||
numMatches = self getCommonPlayerData( "awards", "numMatchesRecorded" );
|
||||
oldAverage = self getCommonPlayerData( "awards", ref );
|
||||
|
||||
oldTotal = oldAverage * numMatches;
|
||||
newAverage = int( (oldTotal + value) / (numMatches + 1) );
|
||||
|
||||
self setCommonPlayerData( "awards", ref, newAverage);
|
||||
}
|
||||
else
|
||||
{
|
||||
recordValue = self getCommonPlayerData( "awards", ref );
|
||||
self setCommonPlayerData( "awards", ref, recordValue + value );
|
||||
}
|
||||
}
|
||||
|
||||
shouldAverageTotal( ref )
|
||||
{
|
||||
switch( ref )
|
||||
{
|
||||
case "scorePerMinute":
|
||||
case "kdratio":
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getFormattedValue( ref, value )
|
||||
{
|
||||
awardFormat = tableLookup( "mp/awardTable.csv", 1, ref, 5 );
|
||||
switch ( awardFormat )
|
||||
{
|
||||
case "float":
|
||||
value = limitDecimalPlaces( value, 2 );
|
||||
value = value * 100;
|
||||
break;
|
||||
case "ratio":
|
||||
case "multi":
|
||||
case "count":
|
||||
case "time":
|
||||
case "distance":
|
||||
case "none":
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
value = int( value );
|
||||
return ( value );
|
||||
}
|
||||
|
||||
highestWins( ref, minAwardable )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player rankingEnabled() && player statValueChanged( ref ) && ( !isDefined( minAwardable ) || player getPlayerStat( ref ) >= minAwardable ) )
|
||||
{
|
||||
player giveAward( ref );
|
||||
player setPersonalBestIfGreater( ref );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lowestWins( ref, maxAwardable)
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player rankingEnabled() && player statValueChanged( ref ) && ( !isDefined( maxAwardable ) || player getPlayerStat( ref ) <= maxAwardable ) )
|
||||
{
|
||||
player giveAward( ref );
|
||||
player setPersonalBestIfLower( ref );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lowestWithHalfPlayedTime( ref )
|
||||
{
|
||||
gameLength = getTimePassed() / 1000;
|
||||
halfGameLength = gameLength * 0.5;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
// hasSpawned check is required or players who pick a team and never spawn can win awards
|
||||
if ( player.hasSpawned && player.timePlayed["total"] >= halfGameLength )
|
||||
{
|
||||
player giveAward( ref );
|
||||
player setPersonalBestIfLower( ref );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statValueChanged( ref )
|
||||
{
|
||||
playervalue = self getPlayerStat( ref );
|
||||
defaultvalue = level.awards[ ref ].defaultvalue;
|
||||
|
||||
if ( playervalue == defaultvalue )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
96
raw/maps/mp/_braggingrights.gsc
Normal file
96
raw/maps/mp/_braggingrights.gsc
Normal file
@ -0,0 +1,96 @@
|
||||
#include maps\mp\_utility;
|
||||
|
||||
resolveBraggingRights()
|
||||
{
|
||||
|
||||
num_bragging_rights = GetNumBraggingRights();
|
||||
brarray = [];
|
||||
|
||||
//clear the array
|
||||
for( i = 0; i < num_bragging_rights; i++ )
|
||||
brarray[i] = [];
|
||||
|
||||
foreach( p in level.players )
|
||||
{
|
||||
if( IsAlive( p ) )
|
||||
{
|
||||
br = p GetBraggingRight();
|
||||
|
||||
if( br < num_bragging_rights ) //which means is valid
|
||||
{
|
||||
//stat_name = TableLookupByRow( "mp/braggingrights.csv", br, 0 );
|
||||
//stat_accessor = TableLookupByRow( "mp/braggingrights.csv", br, 2 );
|
||||
//Print( p.name + " BR:" + br + " stat_name: " + stat_name + " stat_accessor: " + stat_accessor );
|
||||
|
||||
curr_size = brarray[br].size;
|
||||
brarray[br][ curr_size ] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach( i, br in brarray )
|
||||
{
|
||||
if( br.size > 1 )
|
||||
{
|
||||
Print( "Resolving BR conflict at : " + i );
|
||||
stat_accessor = TableLookupByRow( "mp/braggingrights.csv", i, 2 );
|
||||
|
||||
highScore = undefined;
|
||||
winner = undefined;
|
||||
foreach( player in br )
|
||||
{
|
||||
playerValue = player getPlayerStat( stat_accessor );
|
||||
if( !IsDefined( highScore ) || playerValue > highScore )
|
||||
{
|
||||
winner = player;
|
||||
highScore = playerValue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach( player in br )
|
||||
{
|
||||
if( player == winner )
|
||||
{
|
||||
if( !isdefined( player.matchBonus ) )
|
||||
player.matchBonus = 0;
|
||||
|
||||
total_bonus = 0;
|
||||
foreach( p in br )
|
||||
{
|
||||
if( IsDefined( p.matchBonus ) )
|
||||
total_bonus += p.matchBonus;
|
||||
}
|
||||
|
||||
player.matchBonus += total_bonus;
|
||||
|
||||
Print( "Bragging Rights Bonus: " + total_bonus + " For " + player.name + " \n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print( "Bragging Rights LOST by " + player.name + " \n");
|
||||
player.braggingRightsLoser = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach( p in level.players )
|
||||
{
|
||||
if( isdefined( p.braggingRightsLoser ) && p.braggingRightsLoser )
|
||||
p.matchBonus = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GetNumBraggingRights()
|
||||
{
|
||||
line_num = -1;
|
||||
line_val = "temp";
|
||||
while( line_val != "" )
|
||||
{
|
||||
line_num++;
|
||||
line_val = TableLookupByRow( "mp/braggingrights.csv", line_num, 0 );
|
||||
}
|
||||
|
||||
return line_num;
|
||||
}
|
79
raw/maps/mp/_compass.gsc
Normal file
79
raw/maps/mp/_compass.gsc
Normal file
@ -0,0 +1,79 @@
|
||||
setupMiniMap(material)
|
||||
{
|
||||
// use 0 for no required map aspect ratio.
|
||||
requiredMapAspectRatio = level.requiredMapAspectRatio;
|
||||
|
||||
corners = getentarray("minimap_corner", "targetname");
|
||||
if (corners.size != 2)
|
||||
{
|
||||
println("^1Error: There are not exactly two \"minimap_corner\" entities in the map. Could not set up minimap.");
|
||||
return;
|
||||
}
|
||||
|
||||
corner0 = (corners[0].origin[0], corners[0].origin[1], 0);
|
||||
corner1 = (corners[1].origin[0], corners[1].origin[1], 0);
|
||||
|
||||
cornerdiff = corner1 - corner0;
|
||||
|
||||
north = (cos(getnorthyaw()), sin(getnorthyaw()), 0);
|
||||
west = (0 - north[1], north[0], 0);
|
||||
|
||||
// we need the northwest and southeast corners. all we know is that corner0 is opposite of corner1.
|
||||
if (vectordot(cornerdiff, west) > 0) {
|
||||
// corner1 is further west than corner0
|
||||
if (vectordot(cornerdiff, north) > 0) {
|
||||
// corner1 is northwest, corner0 is southeast
|
||||
northwest = corner1;
|
||||
southeast = corner0;
|
||||
}
|
||||
else {
|
||||
// corner1 is southwest, corner0 is northeast
|
||||
side = vecscale(north, vectordot(cornerdiff, north));
|
||||
northwest = corner1 - side;
|
||||
southeast = corner0 + side;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// corner1 is further east than corner0
|
||||
if (vectordot(cornerdiff, north) > 0) {
|
||||
// corner1 is northeast, corner0 is southwest
|
||||
side = vecscale(north, vectordot(cornerdiff, north));
|
||||
northwest = corner0 + side;
|
||||
southeast = corner1 - side;
|
||||
}
|
||||
else {
|
||||
// corner1 is southeast, corner0 is northwest
|
||||
northwest = corner0;
|
||||
southeast = corner1;
|
||||
}
|
||||
}
|
||||
|
||||
// expand map area to fit required aspect ratio
|
||||
if ( requiredMapAspectRatio > 0 )
|
||||
{
|
||||
northportion = vectordot(northwest - southeast, north);
|
||||
westportion = vectordot(northwest - southeast, west);
|
||||
mapAspectRatio = westportion / northportion;
|
||||
if ( mapAspectRatio < requiredMapAspectRatio )
|
||||
{
|
||||
incr = requiredMapAspectRatio / mapAspectRatio;
|
||||
addvec = vecscale( west, westportion * (incr - 1) * 0.5 );
|
||||
}
|
||||
else
|
||||
{
|
||||
incr = mapAspectRatio / requiredMapAspectRatio;
|
||||
addvec = vecscale( north, northportion * (incr - 1) * 0.5 );
|
||||
}
|
||||
northwest += addvec;
|
||||
southeast -= addvec;
|
||||
}
|
||||
|
||||
level.mapSize = vectordot(northwest - southeast, north);
|
||||
|
||||
setMiniMap(material, northwest[0], northwest[1], southeast[0], southeast[1]);
|
||||
}
|
||||
|
||||
vecscale(vec, scalar)
|
||||
{
|
||||
return (vec[0]*scalar, vec[1]*scalar, vec[2]*scalar);
|
||||
}
|
72
raw/maps/mp/_createfx.gsc
Normal file
72
raw/maps/mp/_createfx.gsc
Normal file
@ -0,0 +1,72 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\_createFxMenu;
|
||||
#include common_scripts\_createfx;
|
||||
#include common_scripts\_fx;
|
||||
|
||||
createfx()
|
||||
{
|
||||
level.func_position_player = ::void;
|
||||
level.func_position_player_get = ::func_position_player_get;
|
||||
level.func_loopfxthread = ::loopfxthread;
|
||||
level.func_oneshotfxthread = ::oneshotfxthread;
|
||||
level.func_create_loopsound = ::create_loopsound;
|
||||
// level.func_exploder_preload = ::exploder_before_load;;
|
||||
// level.func_exploder_postload = ::exploder_after_load;;
|
||||
level.func_updatefx = ::restart_fx_looper;
|
||||
level.func_process_fx_rotater = ::process_fx_rotater;
|
||||
level.func_player_speed = ::func_player_speed;
|
||||
|
||||
level.mp_createfx = true;
|
||||
|
||||
// MP only stuff
|
||||
level.callbackStartGameType = ::void;
|
||||
level.callbackPlayerConnect = ::void;
|
||||
level.callbackPlayerDisconnect = ::void;
|
||||
level.callbackPlayerDamage = ::void;
|
||||
level.callbackPlayerKilled = ::void;
|
||||
level.callbackEntityOutOfWorld = ::void;
|
||||
level.callbackCodeEndGame = ::void;
|
||||
level.callbackPlayerLastStand = ::void;
|
||||
level.callbackPlayerConnect = ::Callback_PlayerConnect;
|
||||
level.callbackPlayerMigrated = ::void;
|
||||
|
||||
maps\mp\gametypes\_gameobjects::main( [] ); // clear out extra entities since we're tight on some levels
|
||||
|
||||
thread func_get_level_fx(); // start gettin these on start
|
||||
createfx_common();
|
||||
|
||||
level waittill( "eternity" );
|
||||
}
|
||||
|
||||
func_position_player_get( lastPlayerOrigin )
|
||||
{
|
||||
return level.player.origin;
|
||||
}
|
||||
|
||||
Callback_PlayerConnect()
|
||||
{
|
||||
self waittill( "begin" );
|
||||
|
||||
if ( !isdefined( level.player ) )
|
||||
{
|
||||
spawnpoints = getentarray( "mp_global_intermission", "classname" );
|
||||
spawnangles = ( spawnpoints[0].angles[0], spawnpoints[0].angles[1], 0.0 );
|
||||
self spawn( spawnpoints[0].origin, spawnangles );
|
||||
self updateSessionState( "playing" );
|
||||
self.maxhealth = 10000000;
|
||||
self.health = 10000000;
|
||||
level.player = self;
|
||||
|
||||
thread createFxLogic();
|
||||
}
|
||||
else
|
||||
kick( self GetEntityNumber() );
|
||||
}
|
||||
|
||||
|
||||
func_player_speed()
|
||||
{
|
||||
scale = level._createfx.player_speed / 190;
|
||||
level.player SetMoveSpeedScale( scale );
|
||||
}
|
727
raw/maps/mp/_crib.gsc
Normal file
727
raw/maps/mp/_crib.gsc
Normal file
@ -0,0 +1,727 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
CONST_default_radial_radius = 8; // default distance between radial button and center
|
||||
CONST_radial_center_extrude_dist = 40; // this is the distance between the floating radial center and the oberver's eye
|
||||
CONST_direct_travel = 1; // no path, directly zoom to position
|
||||
CONST_view_travel_unit_dist = 1200; // distance unit per travel interval
|
||||
CONST_view_travel_unit_time = 1; // in seconds per travel interval
|
||||
CONST_blur_strength = 3; // blur strength during view travel, sine curved over travel duration
|
||||
|
||||
init()
|
||||
{
|
||||
precacheShellShock( "frag_grenade_mp" );
|
||||
|
||||
radial_button_definitions(); // define radial button sets and buttons
|
||||
radial_init(); // setup radial button mechanism
|
||||
view_path_setup(); // setup view flight paths
|
||||
player_init();
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == inits ==
|
||||
// ====================================================================================
|
||||
|
||||
radial_button_definitions()
|
||||
{
|
||||
newRadialButtonGroup( "main", "player_view1_start", "player_view1_end" );
|
||||
|
||||
// Main menu's buttons:
|
||||
bWeapons_a = newRadialButton( "main", "Primary Weapon", "radial_weapons_primary", ::action_weapons_primary );
|
||||
bWeapons_b = newRadialButton( "main", "Secondary Weapon", "radial_weapons_secondary", ::action_weapons_secondary );
|
||||
bGears = newRadialButton( "main", "Gears", "radial_gears", ::action_gears );
|
||||
bKillStreaks= newRadialButton( "main", "Kill Streaks", "radial_killstreaks", ::action_killstreak );
|
||||
bLeadboards = newRadialButton( "main", "Leaderboards", "radial_leaderboards", ::action_leaderboards );
|
||||
//
|
||||
|
||||
newRadialButtonGroup( "gears", "player_view2_start", "player_view2_end" );
|
||||
newRadialButtonGroup( "weapons_primary", "player_view3_start", "player_view3_end" );
|
||||
newRadialButtonGroup( "weapons_secondary", "player_view3_start", "player_view3_end" );
|
||||
newRadialButtonGroup( "killstreak", "player_view4_start", "player_view4_end" );
|
||||
newRadialButtonGroup( "leaderboards", "player_view5_start", "player_view5_end" );
|
||||
}
|
||||
|
||||
|
||||
radial_init()
|
||||
{
|
||||
// calculate start & end angles of all buttons for range selection
|
||||
foreach ( button_group in level.radial_button_group )
|
||||
{
|
||||
// sort buttons by angle so we can calculate mid angles in sequence
|
||||
sort_buttons_by_angle( button_group );
|
||||
|
||||
for ( i = 0; i < button_group.size; i ++ )
|
||||
{
|
||||
if ( isdefined( button_group[ i + 1 ] ) )
|
||||
{
|
||||
mid_angle = getMidAngle( button_group[ i ].pos_angle, button_group[ i + 1 ].pos_angle );
|
||||
button_group[ i ].end_angle = mid_angle;
|
||||
button_group[ i + 1 ].start_angle = mid_angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
mid_angle = getMidAngle( button_group[ i ].pos_angle, button_group[ 0 ].pos_angle ) + 180; // +180 to mirror angle
|
||||
if ( mid_angle > 360 )
|
||||
mid_angle -= 360;
|
||||
|
||||
button_group[ i ].end_angle = mid_angle;
|
||||
button_group[ 0 ].start_angle = mid_angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// monitors
|
||||
thread updateSelectedButton();
|
||||
thread watchSelectButtonPress();
|
||||
thread watchBackButtonPress();
|
||||
thread debug_toggle();
|
||||
}
|
||||
|
||||
|
||||
debug_toggle()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level.crib_debug = 1;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.observer ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
button_reset = true;
|
||||
while ( !( level.observer buttonPressed( "BUTTON_Y" ) ) )
|
||||
wait 0.05;
|
||||
|
||||
level.observer playsound("mouse_click");
|
||||
|
||||
if ( button_reset )
|
||||
{
|
||||
level.crib_debug *= -1;
|
||||
button_reset = false;
|
||||
}
|
||||
|
||||
while ( level.observer buttonPressed( "BUTTON_Y" ) )
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
player_init()
|
||||
{
|
||||
level thread onPlayerConnect();
|
||||
level thread return_hud();
|
||||
}
|
||||
|
||||
|
||||
return_hud()
|
||||
{
|
||||
level waittill( "game_ended" );
|
||||
setdvar( "cg_draw2d", 1 );
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
level waittill("connected", player);
|
||||
|
||||
player thread readyPlayer();
|
||||
player waittill( "spawned_player" );
|
||||
|
||||
wait 1;
|
||||
|
||||
player takeallweapons();
|
||||
setdvar( "cg_draw2d", 0 );
|
||||
|
||||
if ( !isdefined( player ) )
|
||||
return;
|
||||
else
|
||||
level.observer = player;
|
||||
|
||||
player thread get_right_stick_angle();
|
||||
|
||||
zoom_to_radial_menu( "main" ); // fly to the first radial menu
|
||||
}
|
||||
|
||||
|
||||
readyPlayer()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
team = "autoassign";
|
||||
|
||||
while(!isdefined(self.pers["team"]))
|
||||
wait .05;
|
||||
|
||||
self notify("menuresponse", game["menu_team"], team);
|
||||
wait 0.5;
|
||||
|
||||
classes = getArrayKeys( level.classMap );
|
||||
okclasses = [];
|
||||
for ( i = 0; i < classes.size; i++ )
|
||||
{
|
||||
if ( !isSubStr( classes[i], "custom" ) )
|
||||
okclasses[ okclasses.size ] = classes[i];
|
||||
}
|
||||
|
||||
assert( okclasses.size );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
class = okclasses[ 0 ];
|
||||
self notify("menuresponse", "changeclass", class);
|
||||
|
||||
self waittill( "spawned_player" );
|
||||
wait ( 0.10 );
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == Radial Mechanics ==
|
||||
// ====================================================================================
|
||||
|
||||
get_right_stick_angle()
|
||||
{
|
||||
// self is user
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
rs_vec = self GetNormalizedMovement();
|
||||
rs_angles = vectortoangles( rs_vec );
|
||||
level.rs_angle = int( rs_angles[1] );
|
||||
|
||||
wait 0.05; // update rate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
newRadialButtonGroup( group_name, view_start, view_end )
|
||||
{
|
||||
if ( isdefined( level.radial_button_group ) && level.radial_button_group.size )
|
||||
assertex( !isdefined( level.radial_button_group[ group_name ] ), "Radial button group: " + group_name + " is already defined." );
|
||||
|
||||
player_view_ent = getent( view_end, "targetname" );
|
||||
assertex( isdefined( player_view_ent ), "Missing player view entity, can not setup radial menu in space" );
|
||||
|
||||
extruded_vec = ( VectorNormalize( AnglesToForward( player_view_ent.angles ) ) * CONST_radial_center_extrude_dist );
|
||||
|
||||
level.radial_button_group[ group_name ] = [];
|
||||
level.radial_button_group_info[ group_name ][ "view_start" ] = view_start;
|
||||
level.radial_button_group_info[ group_name ][ "view_pos" ] = player_view_ent.origin + extruded_vec;
|
||||
level.radial_button_group_info[ group_name ][ "player_view_pos" ] = player_view_ent.origin;
|
||||
level.radial_button_group_info[ group_name ][ "view_angles" ] = player_view_ent.angles;
|
||||
}
|
||||
|
||||
|
||||
newRadialButton( button_group, button_label, button_ent_name, action_func )
|
||||
{
|
||||
assertex( isdefined( level.radial_button_group[ button_group ] ), "Radial button group: " + button_group + " does not exist." );
|
||||
|
||||
ent = getent( button_ent_name, "targetname" );
|
||||
new_button_angle = getRadialAngleFromEnt( button_group, ent );
|
||||
|
||||
button = spawnstruct();
|
||||
button.pos = ent.origin;
|
||||
button.label = button_label;
|
||||
button.font_size = 1;
|
||||
button.font_color = ( 0.5, 0.5, 1 );
|
||||
button.pos_angle = new_button_angle;
|
||||
button.action_func = action_func;
|
||||
button.radius_pos = CONST_default_radial_radius;
|
||||
|
||||
level.radial_button_group[ button_group ][ level.radial_button_group[ button_group ].size ] = button;
|
||||
return button;
|
||||
}
|
||||
|
||||
|
||||
updateSelectedButton()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.radial_button_current_group ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
last_active_button = level.active_button;
|
||||
|
||||
foreach ( button in level.radial_button_group[ level.radial_button_current_group ] )
|
||||
{
|
||||
if ( isInRange( button.start_angle, button.end_angle ) )
|
||||
level.active_button = button;
|
||||
else
|
||||
button.font_color = ( 0.5, 0.5, 1 );
|
||||
}
|
||||
|
||||
if ( isdefined ( level.active_button ) )
|
||||
{
|
||||
level.active_button.font_color = ( 1, 1, 0.5 );
|
||||
|
||||
if ( isdefined( last_active_button ) && last_active_button != level.active_button )
|
||||
level.observer playsound("mouse_over");
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watchSelectButtonPress()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.observer ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
button_reset = true;
|
||||
while ( !( level.observer buttonPressed( "BUTTON_A" ) ) )
|
||||
wait 0.05;
|
||||
|
||||
level.observer playsound("mouse_click");
|
||||
|
||||
if ( isdefined( level.active_button ) && button_reset )
|
||||
{
|
||||
level.active_button notify( "select_button_pressed" );
|
||||
[[level.active_button.action_func]]();
|
||||
button_reset = false;
|
||||
}
|
||||
|
||||
while ( level.observer buttonPressed( "BUTTON_A" ) )
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watchBackButtonPress()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.observer ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
button_reset = true;
|
||||
while ( !( level.observer buttonPressed( "BUTTON_X" ) ) )
|
||||
wait 0.05;
|
||||
|
||||
level.observer playsound("mouse_click");
|
||||
|
||||
if ( button_reset )
|
||||
{
|
||||
action_back();
|
||||
button_reset = false;
|
||||
}
|
||||
|
||||
while ( level.observer buttonPressed( "BUTTON_X" ) )
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sort_buttons_by_angle( button_group )
|
||||
{
|
||||
// button_group is actual array
|
||||
// bubble sort buttons
|
||||
for ( i = 0; i < button_group.size - 1; i++ )
|
||||
{
|
||||
for ( j = 0; j < button_group.size - 1 - i; j++ )
|
||||
{
|
||||
if ( button_group[ j + 1 ].pos_angle < button_group[ j ].pos_angle )
|
||||
button_switch( button_group[ j ], button_group[ j + 1 ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
button_switch( button1, button2 )
|
||||
{
|
||||
temp_pos = button1.pos;
|
||||
temp_label = button1.label;
|
||||
temp_pos_angle = button1.pos_angle;
|
||||
temp_action_func = button1.action_func;
|
||||
temp_radius_pos = button1.radius_pos;
|
||||
|
||||
button1.pos = button2.pos;
|
||||
button1.label = button2.label;
|
||||
button1.pos_angle = button2.pos_angle;
|
||||
button1.action_func = button2.action_func;
|
||||
button1.radius_pos = button2.radius_pos;
|
||||
|
||||
button2.pos = temp_pos;
|
||||
button2.label = temp_label;
|
||||
button2.pos_angle = temp_pos_angle;
|
||||
button2.action_func = temp_action_func;
|
||||
button2.radius_pos = temp_radius_pos;
|
||||
}
|
||||
|
||||
|
||||
draw_radial_buttons( button_group )
|
||||
{
|
||||
foreach ( button in level.radial_button_group[ button_group ] )
|
||||
button thread draw_radial_button( button_group );
|
||||
}
|
||||
|
||||
|
||||
//print3d(<origin>, <text>, <color>, <alpha>, <scale>, <duration> )
|
||||
draw_radial_button( button_group )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "remove_button" );
|
||||
|
||||
floating_origin = level.radial_button_group_info[ button_group ][ "view_pos" ];
|
||||
button_radial_pos = floating_origin + radial_angle_to_vector( self.pos_angle, 4 );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
//line( level.radial_button_group_info[ button_group ][ "view_pos" ], self.pos, ( 0, 1, 0 ), 0.05, false );
|
||||
|
||||
range_color = ( 1, 0, 0 );
|
||||
if ( isInRange( self.start_angle, self.end_angle ) )
|
||||
range_color = ( 1, 1, 0 );
|
||||
|
||||
print3d( self.pos, self.label, self.font_color, 0.75, self.font_size, 1 );
|
||||
|
||||
if ( isdefined( level.crib_debug ) && level.crib_debug > 0 )
|
||||
{
|
||||
print3d( button_radial_pos, ".("+int(self.pos_angle)+")", range_color, 0.75, 0.05, 1 );
|
||||
|
||||
line( floating_origin, floating_origin + radial_angle_to_vector( self.start_angle, 2 ), range_color, 0.05 );
|
||||
line( floating_origin + radial_angle_to_vector( self.start_angle, 2 ), floating_origin + radial_angle_to_vector( self.end_angle, 2 ), range_color, 0.05 );
|
||||
|
||||
// right stick debug ling
|
||||
r_radial_pos = floating_origin + radial_angle_to_vector( level.rs_angle, 2 );
|
||||
line( floating_origin, r_radial_pos, ( 1, 1, 1 ), 0.05 );
|
||||
|
||||
|
||||
}
|
||||
print3d( floating_origin - ( 0, 0, 4.5 ), "(A)=Select (X)=Back", (1, 1, 1), 0.5, 0.05, 1 );
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Zoom_To_Radial_Menu( button_group, reverse )
|
||||
{
|
||||
level.active_button = undefined;
|
||||
|
||||
assertex( isdefined( level.observer ), "Missing observer (connected player), can not attach player to view path" );
|
||||
|
||||
if ( isdefined( level.radial_button_current_group ) && level.radial_button_current_group != "" )
|
||||
{
|
||||
level.radial_button_previous_group = level.radial_button_current_group;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.radial_button_previous_group = "main";
|
||||
level.radial_button_current_group = "main";
|
||||
}
|
||||
|
||||
foreach ( button in level.radial_button_group[ level.radial_button_previous_group ] )
|
||||
button notify( "remove_button" );
|
||||
|
||||
//iPrintLnBold( "flying to: " + button_group );
|
||||
|
||||
if ( isdefined( reverse ) && reverse )
|
||||
level.observer go_path_by_targetname_reverse( level.radial_button_group_info[ level.radial_button_previous_group ][ "view_start" ], button_group );
|
||||
else
|
||||
level.observer go_path_by_targetname( level.radial_button_group_info[ button_group ][ "view_start" ] );
|
||||
|
||||
level thread draw_radial_buttons( button_group );
|
||||
level.radial_button_current_group = button_group;
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================================
|
||||
// == Radial menu - math ==
|
||||
// ====================================================================================
|
||||
|
||||
// edit function with care, returns orientation-sensistive angles
|
||||
getRadialAngleFromEnt( button_group, ent )
|
||||
{
|
||||
assertex( isdefined( level.radial_button_group[ button_group ] ), "getRadialAngleFromEnt: Radial button group does not exist." );
|
||||
assertex( isdefined( ent ), "getRadialAngleFromEnt: Missing entity to be measured." );
|
||||
|
||||
rAngle = level.radial_button_group_info[ button_group ][ "view_angles" ];
|
||||
rPos = level.radial_button_group_info[ button_group ][ "view_pos" ];
|
||||
rPos += ( VectorNormalize( AnglesToForward( rAngle ) ) * CONST_radial_center_extrude_dist );
|
||||
rForward = AnglesToForward( rAngle );
|
||||
rUpwardNorm = VectorNormalize( AnglesToUp( rAngle ) );
|
||||
|
||||
eAngle = ent.angles;
|
||||
ePos = ent.origin;
|
||||
|
||||
projNorm = VectorNormalize( VectorFromLineToPoint( rPos, ( rPos + rForward ), ePos ) );
|
||||
radial_angle = Acos( VectorDot( projNorm, rUpwardNorm ) );
|
||||
|
||||
// vector mirroring
|
||||
if ( VectorDot( AnglesToRight( rAngle ), projNorm ) < 0 )
|
||||
radial_angle = 360 - radial_angle;
|
||||
|
||||
return radial_angle;
|
||||
}
|
||||
|
||||
|
||||
// converts projected angle into player's view plane into a vector
|
||||
radial_angle_to_vector( angle, scaler )
|
||||
{
|
||||
b_angles = ( 270 - ( angle ), 0 , 0 ); // 270 degrees offset to face the player
|
||||
b_vec = AnglesToForward( b_angles );
|
||||
b_vec_norm = VectorNormalize( b_vec );
|
||||
b_vec_final = ( b_vec_norm * scaler );
|
||||
|
||||
return b_vec_final;
|
||||
}
|
||||
|
||||
|
||||
getMidAngle( a1, a2 )
|
||||
{
|
||||
// 0 -> 360 domain
|
||||
mid_angle = ( ( a1 + a2 + 720 ) / 2 ) - 360;
|
||||
return mid_angle;
|
||||
}
|
||||
|
||||
|
||||
isInRange( start_angle, end_angle )
|
||||
{
|
||||
inside_big_angle = ( level.rs_angle > start_angle && level.rs_angle < 360 );
|
||||
inside_small_angle = ( level.rs_angle > 0 && level.rs_angle < end_angle );
|
||||
|
||||
if ( start_angle > end_angle )
|
||||
in_range = ( inside_big_angle || inside_small_angle );
|
||||
else
|
||||
in_range = ( level.rs_angle > start_angle && level.rs_angle < end_angle );
|
||||
|
||||
return in_range;
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == Button action functions ==
|
||||
// ====================================================================================
|
||||
|
||||
// close radial buttons
|
||||
action_back()
|
||||
{
|
||||
//if ( isdefined( level.radial_button_previous_group ) && level.radial_button_previous_group != "" )
|
||||
// zoom_to_radial_menu( level.radial_button_previous_group );
|
||||
/*else*/ if ( isdefined( level.radial_button_current_group ) && level.radial_button_current_group != "main" )
|
||||
zoom_to_radial_menu( "main", true );
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// ==== main ====
|
||||
action_weapons_primary()
|
||||
{
|
||||
iPrintLnBold( "action_weapons_primary" );
|
||||
zoom_to_radial_menu( "weapons_primary" );
|
||||
}
|
||||
|
||||
|
||||
action_weapons_secondary()
|
||||
{
|
||||
iPrintLnBold( "action_weapons_secondary" );
|
||||
zoom_to_radial_menu( "weapons_secondary" );
|
||||
}
|
||||
|
||||
action_gears()
|
||||
{
|
||||
iPrintLnBold( "action_gears" );
|
||||
zoom_to_radial_menu( "gears" );
|
||||
}
|
||||
|
||||
|
||||
action_killstreak()
|
||||
{
|
||||
iPrintLnBold( "action_killstreak" );
|
||||
zoom_to_radial_menu( "killstreak" );
|
||||
}
|
||||
|
||||
|
||||
action_leaderboards()
|
||||
{
|
||||
iPrintLnBold( "action_leaderboards" );
|
||||
zoom_to_radial_menu( "leaderboards" );
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == Pathing functions ==
|
||||
// ====================================================================================
|
||||
|
||||
view_path_setup()
|
||||
{
|
||||
// setup all paths
|
||||
level.view_paths = [];
|
||||
|
||||
// build paths
|
||||
build_path_by_targetname( "player_view1_start" );
|
||||
build_path_by_targetname( "player_view2_start" );
|
||||
build_path_by_targetname( "player_view3_start" );
|
||||
build_path_by_targetname( "player_view4_start" );
|
||||
build_path_by_targetname( "player_view5_start" );
|
||||
}
|
||||
|
||||
|
||||
build_path_by_targetname( path_name )
|
||||
{
|
||||
level.view_paths[ path_name ] = [];
|
||||
|
||||
path_node = getent( path_name, "targetname" );
|
||||
level.view_paths[ path_name ][ level.view_paths[ path_name ].size ] = path_node;
|
||||
|
||||
while( isdefined( path_node ) && isdefined( path_node.target ) )
|
||||
{
|
||||
next_node = getent( path_node.target, "targetname" );
|
||||
level.view_paths[ path_name ][ level.view_paths[ path_name ].size ] = next_node;
|
||||
path_node = next_node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
go_path_by_targetname( path_name )
|
||||
{
|
||||
// self is player
|
||||
if ( !isdefined( level.dummy_mover ) )
|
||||
{
|
||||
start_node = level.view_paths[ path_name ][ 0 ];
|
||||
level.dummy_mover = spawn( "script_model", start_node.origin );
|
||||
level.dummy_mover.angles = start_node.angles;
|
||||
//self AllowedStances( "stand" );
|
||||
self setOrigin( level.dummy_mover.origin - ( 0, 0, 65 ) );
|
||||
self linkTo( level.dummy_mover );
|
||||
wait 0.05;
|
||||
self setplayerangles ( level.dummy_mover.angles );
|
||||
|
||||
self thread force_player_angles();
|
||||
}
|
||||
|
||||
/*
|
||||
travel_time = 1;
|
||||
dist = 0;
|
||||
foreach ( idx, node in level.view_paths[ path_name ] )
|
||||
{
|
||||
if ( isdefined( level.view_paths[ path_name ][ idx + 1 ] ) )
|
||||
dist += abs( distance( level.view_paths[ path_name ][ idx ].origin, level.view_paths[ path_name ][ idx + 1 ].origin ) );
|
||||
}*/
|
||||
|
||||
travel_speed = CONST_view_travel_unit_time;
|
||||
total_distance = abs( distance( level.dummy_mover.origin, level.view_paths[ path_name ][ level.view_paths[ path_name ].size - 1 ].origin ) );
|
||||
travel_speed *= total_distance / CONST_view_travel_unit_dist;
|
||||
travel_speed = max( travel_speed, 0.1 ); // due to repeated button presses, the travel distance can be cut to 0 travel speed at times.
|
||||
|
||||
blur_time = travel_speed;
|
||||
if ( !CONST_direct_travel )
|
||||
blur_time *= travel_speed * ( level.view_paths[ path_name ].size + 1 );
|
||||
self thread blur_sine( CONST_blur_strength, blur_time );
|
||||
|
||||
foreach ( idx, node in level.view_paths[ path_name ] )
|
||||
{
|
||||
//travel_speed = travel_time * ( abs( distance( level.dummy_mover.origin, node.origin ) ) / dist );
|
||||
//travel_speed += 0.05;
|
||||
|
||||
if ( CONST_direct_travel )
|
||||
{
|
||||
if ( idx != level.view_paths[ path_name ].size - 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
//level.dummy_mover MoveTo( node.origin, travel_speed );
|
||||
level.dummy_mover MoveTo( node.origin, travel_speed, travel_speed * 0.5, 0 );
|
||||
level.dummy_mover RotateTo( node.angles, travel_speed, travel_speed * 0.5, 0);
|
||||
wait travel_speed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
go_path_by_targetname_reverse( path_name, back_to_button_group )
|
||||
{
|
||||
assertex( isdefined( level.dummy_mover ), "go_path_by_targetname_reverse called before go_path_by_targetname" );
|
||||
|
||||
travel_speed = CONST_view_travel_unit_time;
|
||||
total_distance = abs( distance( level.dummy_mover.origin, level.radial_button_group_info[ back_to_button_group ][ "player_view_pos" ] ) );
|
||||
travel_speed *= total_distance / CONST_view_travel_unit_dist;
|
||||
travel_speed = max( travel_speed, 0.1 ); // due to repeated button presses, the travel distance can be cut to 0 travel speed at times.
|
||||
|
||||
blur_time = travel_speed;
|
||||
if ( !CONST_direct_travel )
|
||||
blur_time *= travel_speed * ( level.view_paths[ path_name ].size + 1 );
|
||||
self thread blur_sine( CONST_blur_strength, blur_time );
|
||||
|
||||
if ( !CONST_direct_travel )
|
||||
{
|
||||
for ( idx = level.view_paths[ path_name ].size - 1; idx >= 0; idx-- )
|
||||
{
|
||||
node = level.view_paths[ path_name ][ idx ];
|
||||
level.dummy_mover MoveTo( node.origin, travel_speed );
|
||||
level.dummy_mover RotateTo( node.angles, travel_speed );
|
||||
|
||||
//self thread travel_view_fx( travel_speed );
|
||||
wait travel_speed;
|
||||
}
|
||||
}
|
||||
|
||||
self thread blur_sine( CONST_blur_strength, travel_speed );
|
||||
|
||||
pos = level.radial_button_group_info[ back_to_button_group ][ "player_view_pos" ];
|
||||
angle = level.radial_button_group_info[ back_to_button_group ][ "view_angles" ];
|
||||
|
||||
level.dummy_mover MoveTo( pos, travel_speed, travel_speed * 0.5, 0 );
|
||||
level.dummy_mover RotateTo( angle, travel_speed, travel_speed * 0.5, 0 );
|
||||
wait travel_speed;
|
||||
}
|
||||
|
||||
|
||||
travel_view_fx( time )
|
||||
{
|
||||
self setblurforplayer( 20, ( time + 0.2 )/2 );
|
||||
self setblurforplayer( 0, ( time + 0.2 )/2 );
|
||||
self shellshock( "frag_grenade_mp", time + 0.2 );
|
||||
}
|
||||
|
||||
|
||||
blur_sine( strength, time )
|
||||
{
|
||||
time_scaled = int( time/0.05 );
|
||||
|
||||
for( i = 0; i < time_scaled; i ++ )
|
||||
{
|
||||
fraction = ( i / ( time_scaled ) );
|
||||
cos_fraction= sin( 180 * fraction );
|
||||
blur_amount = strength * cos_fraction;
|
||||
|
||||
setdvar( "r_blur", blur_amount );
|
||||
wait 0.05;
|
||||
}
|
||||
setdvar( "r_blur", 0 );
|
||||
}
|
||||
|
||||
|
||||
force_player_angles()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
level.dummy_mover endon( "remove_dummy" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self setplayerangles ( level.dummy_mover.angles );
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
82
raw/maps/mp/_destructables.gsc
Normal file
82
raw/maps/mp/_destructables.gsc
Normal file
@ -0,0 +1,82 @@
|
||||
init()
|
||||
{
|
||||
// level.destructableFX = loadfx("fx/breakables/exp_wall_cinderblock_96");
|
||||
|
||||
ents = getentarray("destructable", "targetname");
|
||||
|
||||
if (getdvar("scr_destructables") == "0")
|
||||
{
|
||||
for (i = 0; i < ents.size; i++)
|
||||
ents[i] delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ents.size; i++)
|
||||
{
|
||||
ents[i] thread destructable_think();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destructable_think()
|
||||
{
|
||||
accumulate = 40;
|
||||
threshold = 0;
|
||||
|
||||
if (isdefined(self.script_accumulate))
|
||||
accumulate = self.script_accumulate;
|
||||
if (isdefined(self.script_threshold))
|
||||
threshold = self.script_threshold;
|
||||
|
||||
if (isdefined(self.script_destructable_area)) {
|
||||
areas = strtok(self.script_destructable_area, " ");
|
||||
for (i = 0; i < areas.size; i++)
|
||||
self blockArea(areas[i]);
|
||||
}
|
||||
|
||||
if ( isdefined( self.script_fxid ) )
|
||||
self.fx = loadfx( self.script_fxid );
|
||||
|
||||
dmg = 0;
|
||||
|
||||
self setcandamage(true);
|
||||
while(1)
|
||||
{
|
||||
self waittill("damage", amount, other);
|
||||
if (amount >= threshold)
|
||||
{
|
||||
dmg += amount;
|
||||
if (dmg >= accumulate)
|
||||
{
|
||||
self thread destructable_destruct();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destructable_destruct()
|
||||
{
|
||||
ent = self;
|
||||
if (isdefined(self.script_destructable_area)) {
|
||||
areas = strtok(self.script_destructable_area, " ");
|
||||
for (i = 0; i < areas.size; i++)
|
||||
self unblockArea(areas[i]);
|
||||
}
|
||||
if ( isdefined( ent.fx ) )
|
||||
playfx( ent.fx, ent.origin + (0,0,6) );
|
||||
ent delete();
|
||||
}
|
||||
|
||||
blockArea(area)
|
||||
{
|
||||
}
|
||||
blockEntsInArea(ents, area)
|
||||
{
|
||||
}
|
||||
unblockArea(area)
|
||||
{
|
||||
}
|
||||
unblockEntsInArea(ents, area)
|
||||
{
|
||||
}
|
337
raw/maps/mp/_dynamic_events.gsc
Normal file
337
raw/maps/mp/_dynamic_events.gsc
Normal file
@ -0,0 +1,337 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
DEFAULT_START_PERCENTAGE = .5;
|
||||
SD_WAIT_TIME = 10;
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: DynamicEvent( dynamicEventStartFunc, dynamicEventResetFunc )"
|
||||
"Summary: A wrapper for handling global Dynamic event functions. The function handles the timing and notifies the level: "dynamic_event_starting" when it's time. Optionally you can pass in a function that is called at the appropriate time as well.
|
||||
"Module: Dynamic Event
|
||||
"CallOn: level
|
||||
"OptionalArg: dynamicEventStartFunc: The function that will play at the start of the dynamic event
|
||||
"OptionalArg: dynamicEventResetFunc: The function that will be called to reset the event. Dev Only.
|
||||
"Example: level thread DynamicEvent( ::dynamicEventStartFunc )
|
||||
"SPMP: multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
DynamicEvent( dynamicEventStartFunc, dynamicEventResetFunc, dynamicEventEndFunc )
|
||||
{
|
||||
if ( getdvarint( "r_reflectionProbeGenerate" ) )//in case maps need to call the dynamic event before load for createfx
|
||||
return;
|
||||
|
||||
if ( IsDefined( level.dynamicEventsType ) )
|
||||
{
|
||||
if ( level.dynamicEventsType == 1 ) // Disabled (Before)
|
||||
return;
|
||||
|
||||
if ( level.dynamicEventsType == 2 ) // Disabled (After)
|
||||
{
|
||||
if( isDefined( dynamicEventEndFunc ) )
|
||||
level [[dynamicEventEndFunc]]();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetDvarIfUninitialized( "scr_dynamic_event_state", "on"); //options are: on, off, and endstate
|
||||
if( getdvar( "scr_dynamic_event_state", "on" ) == "off" )
|
||||
return;
|
||||
else if( getdvar( "scr_dynamic_event_state", "on" ) == "endstate" )
|
||||
{
|
||||
if( isDefined( dynamicEventEndFunc ) )
|
||||
level [[dynamicEventEndFunc]]();
|
||||
return;
|
||||
}
|
||||
|
||||
/#
|
||||
SetDvarIfUninitialized( "scr_dynamic_event_start_perc", DEFAULT_START_PERCENTAGE);
|
||||
SetDvarIfUninitialized( "scr_dynamic_event_start_now", 0);
|
||||
#/
|
||||
|
||||
|
||||
if( !isDefined( level.DynamicEvent ) )
|
||||
level.DynamicEvent = [];
|
||||
|
||||
//round based gamemodes
|
||||
if( level.gametype == "sd" || level.gametype == "sr" )
|
||||
{
|
||||
level thread handle_sd_dynamicEvent( dynamicEventStartFunc, dynamicEventEndFunc );
|
||||
}
|
||||
/* Removing score check. Dynamic events need to be time based only.
|
||||
//do score check
|
||||
else if( level.gametype == "war" || level.gametype == "conf" || level.gametype == "hp" || level.gametype == "dm" )
|
||||
{
|
||||
level thread handle_dynamicEvent( dynamicEventStartFunc, dynamicEventResetFunc );
|
||||
}
|
||||
*/
|
||||
//don't do score check
|
||||
else //ctf, dom, infect, twar
|
||||
{
|
||||
level thread handle_dynamicEvent( dynamicEventStartFunc, dynamicEventResetFunc, false );
|
||||
}
|
||||
|
||||
level thread logDynamicEventStartTime();
|
||||
}
|
||||
|
||||
logDynamicEventStartTime()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level waittill( "dynamic_event_starting" );
|
||||
|
||||
currentDeciSeconds = getTimePassedDeciSecondsIncludingRounds();
|
||||
setMatchData( "dynamicEventTimeDeciSecondsFromMatchStart", clampToShort( currentDeciSeconds) );
|
||||
}
|
||||
|
||||
handle_sd_dynamicEvent( dynamicEventStartFunc, dynamicEventEndFunc )
|
||||
{
|
||||
if( !isDefined( game["dynamicEvent_isInOvertime"] ) )
|
||||
game["dynamicEvent_isInOvertime"] = false;
|
||||
|
||||
if( !isDefined( game["dynamicEvent_hasSwitchedSides"] ) )
|
||||
game["dynamicEvent_hasSwitchedSides"] = false;
|
||||
|
||||
if( !isDefined( game["dynamicEvent_Overtime"] ) )
|
||||
game["dynamicEvent_Overtime"] = false;
|
||||
|
||||
if( !isDefined( game["dynamicEvent_sd_round_counter"] ) )
|
||||
game["dynamicEvent_sd_round_counter"] = 0;
|
||||
else
|
||||
game["dynamicEvent_sd_round_counter"]++;
|
||||
|
||||
/# level thread debug_watch_dynamicEvent_start_now( dynamicEventStartFunc );#/
|
||||
|
||||
//overtime reset
|
||||
if( !game["dynamicEvent_isInOvertime"] && game["dynamicEvent_Overtime"] )
|
||||
{
|
||||
game["dynamicEvent_sd_round_counter"] = 0;
|
||||
game["dynamicEvent_isInOvertime"] = true;
|
||||
game["dynamicEvent_hasSwitchedSides"] = false;
|
||||
}
|
||||
|
||||
//halftime switch sides reset
|
||||
if( isDefined( game["switchedsides"] ) && game["switchedsides"] && !game["dynamicEvent_hasSwitchedSides"] && !game["dynamicEvent_isInOvertime"] )
|
||||
{
|
||||
game["dynamicEvent_sd_round_counter"] = 0;
|
||||
game["dynamicEvent_hasSwitchedSides"] = true;
|
||||
}
|
||||
|
||||
//do event
|
||||
else if( game["dynamicEvent_sd_round_counter"] == 1 )
|
||||
{
|
||||
wait( SD_WAIT_TIME );
|
||||
level notify( "dynamic_event_starting" );
|
||||
if( isDefined( dynamicEventStartFunc ) && isDefined( dynamicEventEndFunc ) )
|
||||
level [[dynamicEventStartFunc]]();
|
||||
}
|
||||
|
||||
//do end
|
||||
else if( game["dynamicEvent_sd_round_counter"] > 1 )
|
||||
{
|
||||
if( isDefined( dynamicEventStartFunc ) && isDefined( dynamicEventEndFunc ) )
|
||||
level [[dynamicEventEndFunc]]();
|
||||
}
|
||||
}
|
||||
|
||||
debug_watch_dynamicEvent_start_now( dynamicEventStartFunc )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level notify( "waching_dynamicevent_dvar" );
|
||||
level endon( "waching_dynamicevent_dvar" );
|
||||
|
||||
if( GetDvarInt( "scr_dynamic_event_start_now" ) == 1 )
|
||||
{
|
||||
setDvar( "scr_dynamic_event_start_now", 0 );
|
||||
}
|
||||
|
||||
while( true )
|
||||
{
|
||||
if( GetDvarInt( "scr_dynamic_event_start_now" ) == 1 && game["dynamicEvent_sd_round_counter"] == 0 )
|
||||
{
|
||||
level notify( "dynamic_event_starting" );
|
||||
if( isDefined( dynamicEventStartFunc ) )
|
||||
level [[dynamicEventStartFunc]]();
|
||||
game["dynamicEvent_sd_round_counter"] = 1;
|
||||
while( GetDvarInt( "scr_dynamic_event_start_now", 1 ) == 1 )
|
||||
{
|
||||
wait( 1 );
|
||||
}
|
||||
}
|
||||
wait( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
handle_dynamicEvent( dynamicEventStartFunc, dynamicEventResetFunc, doScoreCheck )
|
||||
{
|
||||
time_limit = getDynamicEventTimeLimit();
|
||||
start_time = getDynamicEventStartTime();
|
||||
|
||||
score = undefined;
|
||||
score_limit = getScoreLimit();
|
||||
if( !isDefined( doScoreCheck ) )
|
||||
doScoreCheck = true;
|
||||
|
||||
while( time_limit > start_time && ( !doScoreCheck || ( !isDefined( score ) || score <= score_limit*level.DynamicEvent["start_percent"] ) ) )
|
||||
{
|
||||
/#
|
||||
start_percent = GetDvarfloat( "scr_dynamic_event_start_perc", level.DynamicEvent["start_percent"] );
|
||||
if( start_percent != level.DynamicEvent["start_percent"] )
|
||||
{
|
||||
setDynamicEventStartPercent( start_percent );
|
||||
start_time = getDynamicEventStartTime();
|
||||
}
|
||||
|
||||
if( GetDvarInt( "scr_dynamic_event_start_now" ) == 1 )
|
||||
time_limit = start_time;
|
||||
#/
|
||||
|
||||
wait( 1 );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
time_limit = time_limit - 1;
|
||||
score = GetDynamicEventHighestScore();
|
||||
}
|
||||
|
||||
|
||||
level notify( "dynamic_event_starting" );
|
||||
|
||||
if( isDefined( dynamicEventStartFunc ) )
|
||||
level [[dynamicEventStartFunc]]();
|
||||
|
||||
/#
|
||||
while ( IsDefined(dynamicEventResetFunc) )
|
||||
{
|
||||
SetDvar("scr_dynamic_event_start_now", 0);
|
||||
while( !GetDvarInt("scr_dynamic_event_start_now", 0) )
|
||||
waitframe();
|
||||
|
||||
level notify( "dynamic_event_reset" );
|
||||
level [[dynamicEventResetFunc]]();
|
||||
|
||||
SetDvar("scr_dynamic_event_start_now", 0);
|
||||
while( !GetDvarInt("scr_dynamic_event_start_now", 0) )
|
||||
waitframe();
|
||||
|
||||
level notify( "dynamic_event_starting" );
|
||||
|
||||
if( isDefined( dynamicEventStartFunc ) )
|
||||
level [[dynamicEventStartFunc]]();
|
||||
}
|
||||
#/
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: setDynamicEventStartPercent( percent )"
|
||||
"Summary: Sets when the dynamic event should start."
|
||||
"Module: Dynamic Event"
|
||||
"CallOn: level"
|
||||
"OptionalArg: percent: The percentage of the total match time to start the event. expecting it to be between 0 and 1. default is .5 (50 percent)."
|
||||
"Example: level setDynamicEventStartPercent( .5 )"
|
||||
"SPMP: multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
setDynamicEventStartPercent( percent )
|
||||
{
|
||||
if( !isDefined( percent ) )
|
||||
percent = DEFAULT_START_PERCENTAGE;
|
||||
|
||||
if( percent < 0 || percent > 1 )
|
||||
AssertMsg( "expecting a percent between 0 and 1" );
|
||||
|
||||
level.DynamicEvent["start_percent"] = percent;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: getDynamicEventStartTime()"
|
||||
"Summary: gets the dynamic event start time. if it isn't set, it will set it."
|
||||
"Module: Dynamic Event"
|
||||
"CallOn: level"
|
||||
"Example: start_time = getDynamicEventStartTime()"
|
||||
"SPMP: multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
getDynamicEventStartTime()
|
||||
{
|
||||
if( !isDefined( level.DynamicEvent["start_percent"] ) )
|
||||
setDynamicEventStartPercent();
|
||||
|
||||
time_limit = getDynamicEventTimeLimit();
|
||||
|
||||
start_time = time_limit - ( time_limit * level.DynamicEvent["start_percent"] ); // set the move time to a percentage of the timelimit.
|
||||
|
||||
return start_time;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: GetDynamicEventHighestScore()"
|
||||
"Summary: gets the highest score of one of the teams if teambased. If not teambased it uses player scores. "
|
||||
"Module: Dynamic Event"
|
||||
"CallOn: level"
|
||||
"Example: start_time = GetDynamicEventHighestScore()"
|
||||
"SPMP: multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
GetDynamicEventHighestScore()
|
||||
{
|
||||
score = undefined;
|
||||
if( level.teamBased )
|
||||
{
|
||||
team = maps\mp\gametypes\_gamescore::getWinningTeam();
|
||||
if( isDefined( team ) && team == "none" && isDefined( level.teamNameList ) )
|
||||
score = maps\mp\gametypes\_gamescore::_getTeamScore( level.teamNameList[0] );
|
||||
else if( isDefined( team ) )
|
||||
score = maps\mp\gametypes\_gamescore::_getTeamScore( team );
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
player = maps\mp\gametypes\_gamescore::getHighestScoringPlayer();
|
||||
if( !isDefined( player ) && isDefined( level.players ) && level.players.size > 0 )
|
||||
score = maps\mp\gametypes\_gamescore::_getPlayerScore( level.players[0] );
|
||||
else if( isDefined( player ) )
|
||||
score = maps\mp\gametypes\_gamescore::_getPlayerScore( player );
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: getDynamicEventTimeLimit()"
|
||||
"Summary: gets the time limit in seconds for the match, used for the dynamic event timing. If there is infinite time, the time limit for the dynamic event will use 600 secs (10 mins)"
|
||||
"Module: Dynamic Event"
|
||||
"CallOn: level"
|
||||
"Example: time_limit = getDynamicEventTimeLimit()"
|
||||
"SPMP: multiplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
getDynamicEventTimeLimit()
|
||||
{
|
||||
time_limit = getTimeLimit();
|
||||
if ( time_limit == 0 )//in case the time is infinite time (private match)
|
||||
time_limit = 10 * 60; // get the game type timelimit in seconds
|
||||
else
|
||||
time_limit = time_limit * 60; // get the game type timelimit in seconds
|
||||
|
||||
has_half_time = getHalfTime();
|
||||
if( isDefined( has_half_time ) && has_half_time )
|
||||
time_limit = time_limit/2;
|
||||
|
||||
return time_limit;
|
||||
}
|
1800
raw/maps/mp/_dynamic_world.gsc
Normal file
1800
raw/maps/mp/_dynamic_world.gsc
Normal file
File diff suppressed because it is too large
Load Diff
232
raw/maps/mp/_empgrenade.gsc
Normal file
232
raw/maps/mp/_empgrenade.gsc
Normal file
@ -0,0 +1,232 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
//precacheShellshock("flashbang_mp");
|
||||
PrecacheDigitalDistortCodeAssets();
|
||||
thread onPlayerConnect();
|
||||
PreCacheString( &"MP_EMP_REBOOTING" );
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
self thread monitorEMPGrenade();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
monitorEMPGrenade()
|
||||
{
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
self endon("faux_spawn");
|
||||
self.empEndTime = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill( "emp_grenaded", attacker );
|
||||
|
||||
if ( !isalive( self ) )
|
||||
continue;
|
||||
|
||||
if ( isDefined( self.usingRemote ) )
|
||||
continue;
|
||||
|
||||
//MW3 emp resistance perk
|
||||
if ( self _hasPerk( "specialty_empimmune" ) )
|
||||
continue;
|
||||
|
||||
hurtVictim = true;
|
||||
hurtAttacker = false;
|
||||
|
||||
assert(isdefined(self.pers["team"]));
|
||||
|
||||
if (level.teamBased && isdefined(attacker) && isdefined(attacker.pers["team"]) && attacker.pers["team"] == self.pers["team"] && attacker != self)
|
||||
{
|
||||
if(level.friendlyfire == 0) // no FF
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(level.friendlyfire == 1) // FF
|
||||
{
|
||||
hurtattacker = false;
|
||||
hurtvictim = true;
|
||||
}
|
||||
else if(level.friendlyfire == 2) // reflect
|
||||
{
|
||||
hurtvictim = false;
|
||||
hurtattacker = true;
|
||||
}
|
||||
else if(level.friendlyfire == 3) // share
|
||||
{
|
||||
hurtattacker = true;
|
||||
hurtvictim = true;
|
||||
}
|
||||
}
|
||||
else if( isDefined(attacker) )
|
||||
{
|
||||
attacker notify( "emp_hit" );
|
||||
if( attacker != self )
|
||||
attacker maps\mp\gametypes\_missions::processChallenge( "ch_onthepulse" );
|
||||
}
|
||||
|
||||
if ( hurtvictim && isDefined(self) )
|
||||
self thread applyEMP();
|
||||
if ( hurtattacker && isDefined(attacker) )
|
||||
attacker thread applyEMP();
|
||||
}
|
||||
}
|
||||
|
||||
emp_hide_HUD( id )
|
||||
{
|
||||
maps\mp\gametypes\_scrambler::playerSetHUDEmpScrambledOff( id );
|
||||
}
|
||||
|
||||
applyEMP()
|
||||
{
|
||||
self notify( "applyEmp" );
|
||||
self endon( "applyEmp" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
wait .05;
|
||||
self.empDuration = 3;
|
||||
|
||||
HUDtext = 2;
|
||||
|
||||
// disable exo when emp'd, if gamemode appropriate
|
||||
if( isAugmentedGameMode() )
|
||||
{
|
||||
HUDtext = 1;
|
||||
self playerAllowHighJump(false, "empgrenade");
|
||||
self playerAllowHighJumpDrop(false, "empgrenade");
|
||||
self playerAllowBoostJump(false, "empgrenade");
|
||||
self playerAllowPowerSlide(false, "empgrenade");
|
||||
self playerAllowDodge(false, "empgrenade");
|
||||
}
|
||||
|
||||
self.empGrenaded = true;
|
||||
//self shellshock( "flashbang_mp", 1 );
|
||||
self.empEndTime = getTime() + (self.empDuration * 1000);
|
||||
id = maps\mp\gametypes\_scrambler::playerSetHUDEmpScrambled( self.empEndTime, HUDtext, "emp" );
|
||||
self thread digitalDistort(self.empDuration, id);
|
||||
|
||||
self thread empRumbleLoop( .75 );
|
||||
self setEMPJammed( true );
|
||||
|
||||
self thread empGrenadeDeathWaiter( id );
|
||||
wait ( self.empDuration );
|
||||
self notify( "empGrenadeTimedOut" );
|
||||
self checkToTurnOffEmp( id );
|
||||
}
|
||||
|
||||
digitalDistort( empDuration, id )
|
||||
{
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
self endon("faux_spawn");
|
||||
self endon("joined_team");
|
||||
|
||||
self DigitalDistortSetMaterial("digital_distort_mp");
|
||||
self DigitalDistortSetParams(1.0, 1.0);
|
||||
|
||||
self thread watchDistortDisconnectDeath( id );
|
||||
|
||||
wait( 0.1 );
|
||||
|
||||
//self DigitalDistortSetMaterial("digital_distort_persistent_mp");
|
||||
fadeTime = empDuration;
|
||||
maxIntensity = 0.95;
|
||||
minIntensity = 0.2;
|
||||
intensityRange = maxIntensity - minIntensity;
|
||||
increment = 0.1;
|
||||
currentIntensity = maxIntensity;
|
||||
while ( fadeTime > 0 )
|
||||
{
|
||||
currentIntensity = intensityRange * ( fadeTime / empDuration ) + minIntensity;
|
||||
self DigitalDistortSetParams( currentIntensity, 1.0 );
|
||||
fadeTime -= increment;
|
||||
wait( increment );
|
||||
}
|
||||
self DigitalDistortSetParams( 0.0, 0.0 );
|
||||
}
|
||||
|
||||
watchDistortDisconnectDeath( id )
|
||||
{
|
||||
self waittill_any( "death", "disconnect", "faux_spawn", "joined_team" );
|
||||
if ( IsDefined( self ) )
|
||||
{
|
||||
self DigitalDistortSetParams(0.0, 0.0);
|
||||
emp_hide_HUD( id );
|
||||
}
|
||||
}
|
||||
|
||||
empGrenadeDeathWaiter( id )
|
||||
{
|
||||
self notify( "empGrenadeDeathWaiter" );
|
||||
self endon( "empGrenadeDeathWaiter" );
|
||||
|
||||
self endon( "empGrenadeTimedOut" );
|
||||
|
||||
self waittill( "death" );
|
||||
self checkToTurnOffEmp( id );
|
||||
}
|
||||
|
||||
checkToTurnOffEmp( id )
|
||||
{
|
||||
self.empGrenaded = false;
|
||||
|
||||
self setEMPJammed( false );
|
||||
|
||||
// enable exo, if gamemode appropriate
|
||||
if( isAugmentedGameMode() )
|
||||
{
|
||||
self playerAllowHighJump(true, "empgrenade");
|
||||
self playerAllowHighJumpDrop(true, "empgrenade");
|
||||
self playerAllowBoostJump(true, "empgrenade");
|
||||
self playerAllowPowerSlide(true, "empgrenade");
|
||||
self playerAllowDodge(true, "empgrenade");
|
||||
}
|
||||
self DigitalDistortSetParams(0.0, 0.0);
|
||||
self emp_hide_HUD( id );
|
||||
}
|
||||
|
||||
empRumbleLoop( duration )
|
||||
{
|
||||
self endon("emp_rumble_loop");
|
||||
self notify("emp_rumble_loop");
|
||||
|
||||
goalTime = getTime() + duration * 1000;
|
||||
|
||||
while ( getTime() < goalTime )
|
||||
{
|
||||
self PlayRumbleOnEntity( "damage_heavy" );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isEMPGrenaded()
|
||||
{
|
||||
return isDefined( self.empEndTime ) && gettime() < self.empEndTime;
|
||||
}
|
414
raw/maps/mp/_entityheadicons.gsc
Normal file
414
raw/maps/mp/_entityheadicons.gsc
Normal file
@ -0,0 +1,414 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
init()
|
||||
{
|
||||
if (isdefined(level.initedEntityHeadIcons))
|
||||
return;
|
||||
level.initedEntityHeadIcons = true;
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
foreach ( teamName in level.teamNameList )
|
||||
{
|
||||
str_team_headicon = "entity_headicon_" + teamName;
|
||||
game[ str_team_headicon ] = maps\mp\gametypes\_teams::MT_getTeamHeadIcon( teamName );
|
||||
precacheShader( game[ str_team_headicon ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
game["entity_headicon_allies"] = maps\mp\gametypes\_teams::getTeamHeadIcon( "allies" );
|
||||
game["entity_headicon_axis"] = maps\mp\gametypes\_teams::getTeamHeadIcon( "axis" );
|
||||
|
||||
precacheShader( game["entity_headicon_allies"] );
|
||||
precacheShader( game["entity_headicon_axis"] );
|
||||
}
|
||||
}
|
||||
|
||||
// This can show to single players or to teams. Showing to a single player destroys instances of
|
||||
// the icon that are shown to their team. Showing to a team destroys instances of the icon that
|
||||
// are shown to players on that team
|
||||
setHeadIcon( showTo, icon, offset, width, height, archived, delay, constantSize, pinToScreenEdge, fadeOutPinnedIcon, is3D, targetTag )
|
||||
{
|
||||
if ( IsGameParticipant( showTo ) && !IsPlayer( showTo ) )
|
||||
return; // Doesn't work for Agents, etc.
|
||||
|
||||
if ( !isDefined( self.entityHeadIcons ) )
|
||||
self.entityHeadIcons = [];
|
||||
|
||||
if( !IsDefined( archived ) )
|
||||
archived = true;
|
||||
|
||||
if( !IsDefined( delay ) )
|
||||
delay = 0.05;
|
||||
|
||||
if( !IsDefined( constantSize ) )
|
||||
constantSize = true;
|
||||
|
||||
if( !IsDefined( pinToScreenEdge ) )
|
||||
pinToScreenEdge = true;
|
||||
|
||||
if( !IsDefined( fadeOutPinnedIcon ) )
|
||||
fadeOutPinnedIcon = false;
|
||||
|
||||
if( !IsDefined( is3D ) )
|
||||
is3D = true;
|
||||
|
||||
if( !IsDefined( targetTag ) )
|
||||
targetTag = "";
|
||||
|
||||
if ( !isPlayer( showTo ) && showTo == "none" )
|
||||
{
|
||||
foreach ( key, headIcon in self.entityHeadIcons )
|
||||
{
|
||||
// TODO: remove and fix properly after ship
|
||||
if ( isDefined( headIcon ) )
|
||||
headIcon destroy();
|
||||
|
||||
self.entityHeadIcons[ key ] = undefined;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isPlayer( showTo ) )
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcons[ showTo.guid ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ showTo.guid ] destroy();
|
||||
self.entityHeadIcons[ showTo.guid ] = undefined;
|
||||
}
|
||||
|
||||
if ( icon == "" )
|
||||
return;
|
||||
|
||||
// remove from team or we'd have two icons
|
||||
if ( isDefined( self.entityHeadIcons[ showTo.team ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ showTo.team ] destroy();
|
||||
self.entityHeadIcons[ showTo.team ] = undefined;
|
||||
}
|
||||
|
||||
headIcon = newClientHudElem( showTo );
|
||||
self.entityHeadIcons[ showTo.guid ] = headIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( showTo == "axis" || showTo == "allies" || isSubStr( showTo, "team_" ) );
|
||||
assert( level.teamBased );
|
||||
|
||||
if ( isDefined( self.entityHeadIcons[ showTo ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ showTo ] destroy();
|
||||
self.entityHeadIcons[ showTo ] = undefined;
|
||||
}
|
||||
|
||||
if ( icon == "" )
|
||||
return;
|
||||
|
||||
foreach ( key, hudIcon in self.entityHeadIcons )
|
||||
{
|
||||
if ( key == "axis" || key == "allies" )
|
||||
continue;
|
||||
|
||||
player = getPlayerForGuid( key );
|
||||
if ( player.team == showTo )
|
||||
{
|
||||
self.entityHeadIcons[ key ] destroy();
|
||||
self.entityHeadIcons[ key ] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
headIcon = newTeamHudElem( showTo );
|
||||
self.entityHeadIcons[ showTo ] = headIcon;
|
||||
}
|
||||
|
||||
if ( !isDefined( width ) || !isDefined( height ) )
|
||||
{
|
||||
width = 10;
|
||||
height = 10;
|
||||
}
|
||||
|
||||
headIcon.archived = archived;
|
||||
headIcon.alpha = 0.85;
|
||||
headIcon setShader( icon, width, height );
|
||||
headIcon setWaypoint( constantSize, pinToScreenEdge, fadeOutPinnedIcon, is3D );
|
||||
|
||||
if( targetTag == "" )
|
||||
{
|
||||
headIcon.x = self.origin[0] + offset[0];
|
||||
headIcon.y = self.origin[1] + offset[1];
|
||||
headIcon.z = self.origin[2] + offset[2];
|
||||
headIcon thread keepPositioned( self, offset, delay );
|
||||
}
|
||||
else
|
||||
{
|
||||
headIcon.x = offset[0];
|
||||
headIcon.y = offset[1];
|
||||
headIcon.z = offset[2];
|
||||
headIcon SetTargetEnt( self, targetTag );
|
||||
}
|
||||
|
||||
self thread destroyIconsOnDeath();
|
||||
if ( isPlayer( showTo ) )
|
||||
headIcon thread destroyOnOwnerDisconnect( showTo );
|
||||
if ( isPlayer( self ) )
|
||||
headIcon thread destroyOnOwnerDisconnect( self );
|
||||
|
||||
return headIcon;
|
||||
}
|
||||
|
||||
|
||||
destroyOnOwnerDisconnect( owner )
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
owner waittill ( "disconnect" );
|
||||
|
||||
self destroy();
|
||||
}
|
||||
|
||||
|
||||
destroyIconsOnDeath()
|
||||
{
|
||||
self notify ( "destroyIconsOnDeath" );
|
||||
self endon ( "destroyIconsOnDeath" );
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
foreach ( key, headIcon in self.entityHeadIcons )
|
||||
{
|
||||
// TODO: remove and fix properly after ship
|
||||
if( !isDefined(headIcon) ) //needed for FFA host migration (when host has active head icons)
|
||||
continue;
|
||||
|
||||
headIcon destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
keepPositioned( owner, offset, delay )
|
||||
{
|
||||
self endon ( "death" );
|
||||
owner endon ( "death" );
|
||||
owner endon ( "disconnect" );
|
||||
|
||||
pos = owner.origin;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if( !IsDefined( owner ) )
|
||||
return;
|
||||
|
||||
if ( pos != owner.origin )
|
||||
{
|
||||
pos = owner.origin;
|
||||
|
||||
self.x = pos[0] + offset[0];
|
||||
self.y = pos[1] + offset[1];
|
||||
self.z = pos[2] + offset[2];
|
||||
}
|
||||
|
||||
if ( delay > 0.05 )
|
||||
{
|
||||
self.alpha = 0.85;
|
||||
self FadeOverTime( delay );
|
||||
self.alpha = 0;
|
||||
}
|
||||
|
||||
wait delay;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//facing will always place the marker offset from the top of the actor.
|
||||
//this is useful if your entity can attach to walls or ceilings
|
||||
setTeamHeadIcon( team, offset, targetTag, mountedObject ) // "allies", "axis", "all", "none"
|
||||
{
|
||||
if ( !level.teamBased )
|
||||
return;
|
||||
|
||||
if( !IsDefined( targetTag ) )
|
||||
targetTag = "";
|
||||
|
||||
if ( !isDefined( self.entityHeadIconTeam ) )
|
||||
{
|
||||
self.entityHeadIconTeam = "none";
|
||||
self.entityHeadIcon = undefined;
|
||||
}
|
||||
|
||||
if(IsDefined(mountedObject)&&mountedObject == false)
|
||||
facing = undefined;
|
||||
|
||||
shader = game["entity_headicon_" + team];
|
||||
|
||||
self.entityHeadIconTeam = team;
|
||||
|
||||
if ( isDefined( offset ) )
|
||||
self.entityHeadIconOffset = offset;
|
||||
else
|
||||
self.entityHeadIconOffset = (0,0,0);
|
||||
|
||||
self notify( "kill_entity_headicon_thread" );
|
||||
|
||||
if ( team == "none" )
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcon ) )
|
||||
self.entityHeadIcon destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
headIcon = newTeamHudElem( team );
|
||||
headIcon.archived = true;
|
||||
headIcon.alpha = .8;
|
||||
headIcon setShader( shader, 10, 10 );
|
||||
headIcon setWaypoint( false, false, false, true );
|
||||
self.entityHeadIcon = headIcon;
|
||||
|
||||
if(!IsDefined(mountedObject))
|
||||
{
|
||||
if( targetTag == "" )
|
||||
{
|
||||
headIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
|
||||
headIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
|
||||
headIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
|
||||
self thread keepIconPositioned();
|
||||
}
|
||||
else
|
||||
{
|
||||
headIcon.x = self.entityHeadIconOffset[0];
|
||||
headIcon.y = self.entityHeadIconOffset[1];
|
||||
headIcon.z = self.entityHeadIconOffset[2];
|
||||
headIcon SetTargetEnt( self, targetTag );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
up = AnglesToUp( self.angles );
|
||||
markerPosOverride = self.origin + ( up * 28 );
|
||||
|
||||
if( targetTag == "" )
|
||||
{
|
||||
headIcon.x = markerPosOverride[0];
|
||||
headIcon.y = markerPosOverride[1];
|
||||
headIcon.z = markerPosOverride[2];
|
||||
self thread keepIconPositioned( mountedObject );
|
||||
}
|
||||
else
|
||||
{ headIcon.x = markerPosOverride[0];
|
||||
headIcon.y = markerPosOverride[1];
|
||||
headIcon.z = markerPosOverride[2];
|
||||
headIcon SetTargetEnt( self, targetTag );
|
||||
}
|
||||
}
|
||||
|
||||
self thread destroyHeadIconsOnDeath();
|
||||
}
|
||||
|
||||
setPlayerHeadIcon( player, offset, targetTag ) // "allies", "axis", "all", "none"
|
||||
{
|
||||
if ( level.teamBased )
|
||||
return;
|
||||
|
||||
if( !IsDefined( targetTag ) )
|
||||
targetTag = "";
|
||||
|
||||
if ( !isDefined( self.entityHeadIconTeam ) )
|
||||
{
|
||||
self.entityHeadIconTeam = "none";
|
||||
self.entityHeadIcon = undefined;
|
||||
}
|
||||
|
||||
self notify( "kill_entity_headicon_thread" );
|
||||
|
||||
if ( !isDefined( player ) )
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcon ) )
|
||||
self.entityHeadIcon destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
team = player.team;
|
||||
self.entityHeadIconTeam = team;
|
||||
|
||||
if ( isDefined( offset ) )
|
||||
self.entityHeadIconOffset = offset;
|
||||
else
|
||||
self.entityHeadIconOffset = (0,0,0);
|
||||
|
||||
shader = game["entity_headicon_" + team];
|
||||
|
||||
headIcon = newClientHudElem( player );
|
||||
headIcon.archived = true;
|
||||
headIcon.alpha = .8;
|
||||
headIcon setShader( shader, 10, 10 );
|
||||
headIcon setWaypoint( false, false, false, true );
|
||||
|
||||
self.entityHeadIcon = headIcon;
|
||||
|
||||
if( targetTag == "" )
|
||||
{
|
||||
headIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
|
||||
headIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
|
||||
headIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
|
||||
self thread keepIconPositioned();
|
||||
}
|
||||
else
|
||||
{
|
||||
headIcon.x = self.entityHeadIconOffset[0];
|
||||
headIcon.y = self.entityHeadIconOffset[1];
|
||||
headIcon.z = self.entityHeadIconOffset[2];
|
||||
headIcon SetTargetEnt( self, targetTag );
|
||||
}
|
||||
self thread destroyHeadIconsOnDeath();
|
||||
}
|
||||
|
||||
keepIconPositioned( mountedObject )
|
||||
{
|
||||
self endon( "kill_entity_headicon_thread" );
|
||||
self endon( "death" );
|
||||
|
||||
pos = self.origin;
|
||||
while(1)
|
||||
{
|
||||
if ( pos != self.origin )
|
||||
{
|
||||
self updateHeadIconOrigin( mountedObject );
|
||||
pos = self.origin;
|
||||
}
|
||||
wait .05;
|
||||
}
|
||||
}
|
||||
|
||||
destroyHeadIconsOnDeath()
|
||||
{
|
||||
self endon( "kill_entity_headicon_thread" );
|
||||
self waittill ( "death" );
|
||||
|
||||
// TODO: remove and fix properly after ship
|
||||
if( !isDefined(self.entityHeadIcon) )
|
||||
return;
|
||||
|
||||
self.entityHeadIcon destroy();
|
||||
}
|
||||
|
||||
|
||||
updateHeadIconOrigin( mountedObject )
|
||||
{
|
||||
if(!IsDefined(mountedObject))
|
||||
{
|
||||
self.entityHeadIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
|
||||
self.entityHeadIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
|
||||
self.entityHeadIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
up = AnglesToUp( self.angles );
|
||||
markerPosOverride = self.origin + ( up * 28 );
|
||||
|
||||
self.entityHeadIcon.x = markerPosOverride[0];
|
||||
self.entityHeadIcon.y = markerPosOverride[1];
|
||||
self.entityHeadIcon.z = markerPosOverride[2];
|
||||
}
|
||||
}
|
1849
raw/maps/mp/_events.gsc
Normal file
1849
raw/maps/mp/_events.gsc
Normal file
File diff suppressed because it is too large
Load Diff
239
raw/maps/mp/_exo_battery.gsc
Normal file
239
raw/maps/mp/_exo_battery.gsc
Normal file
@ -0,0 +1,239 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: update_exo_battery_hud( <weapon_name> , <additional_endon_string> )"
|
||||
"Summary: updates the LUI battery HUD."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"MandatoryArg: <weapon_name>: string - the name of the weapon for which the player is using battery energy."
|
||||
"MandatoryArg: <weapon_active_flag>: bool - tracks the active state of the weapon. Ends updating "
|
||||
"Example: player thread update_exo_battery_hud( "exoshield_equipment_mp" );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
update_exo_battery_hud( weapon_name )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "kill_battery" );
|
||||
|
||||
if ( !IsPlayer(self) )
|
||||
return;
|
||||
|
||||
while ( self get_exo_ability_hud_omnvar_value( weapon_name, "ui_exo_battery_toggle" ) == 1 )
|
||||
{
|
||||
battery_energy = self BatteryGetCharge( weapon_name );
|
||||
// Setting ui_exo_battery_level to the current energy level.
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( weapon_name, "ui_exo_battery_level", battery_energy );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
low_exo_battery_energy_sfx( weapon_active_flag ) // self = player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "kill_battery" );
|
||||
|
||||
self.is_playing_low_battery_sfx = false;
|
||||
|
||||
while ( isReallyAlive( self ) && weapon_active_flag == true )
|
||||
{
|
||||
battery_energy = self BatteryGetCharge( weapon_name );
|
||||
battery_capacity = self BatteryGetSize( weapon_name );
|
||||
|
||||
if ( !self.is_playing_low_battery_sfx && battery_energy < battery_capacity / 4 )
|
||||
{
|
||||
self.is_playing_low_battery_sfx = true;
|
||||
|
||||
self thread play_low_energy_sfx();
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
// Turning off the low battery SFX.
|
||||
self notify( "stop_low_battery_sfx" );
|
||||
self.is_playing_low_battery_sfx = false;
|
||||
}
|
||||
|
||||
|
||||
play_low_energy_sfx() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "stop_low_battery_sfx" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self PlayLocalSound( "mp_exo_bat_low" );
|
||||
|
||||
wait( 0.6 );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: set_exo_hud_omnvar( <weapon_name> , <omnvar_name> , <omnvar_value> )"
|
||||
"Summary: checks whether the player has the given weapon in their tactical or lethal slot and changes the appropriate omnvar."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"MandatoryArg: <weapon_name>: string - name of the weapon you want to check for."
|
||||
"MandatoryArg: <omnvar_name>: string - omnvar that you want to set."
|
||||
"MandatoryArg: <omnvar_value>: value to which you want to set the omnvar."
|
||||
"Example: player set_exo_hud_omnvar( "exoshield_equipment_mp", "ui_exo_battery_level", 0 );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
set_exo_ability_hud_omnvar( weapon_name, omnvar_name, omnvar_value )
|
||||
{
|
||||
if ( self GetTacticalWeapon() == weapon_name )
|
||||
{
|
||||
self SetClientOmnvar( omnvar_name + "0", omnvar_value );
|
||||
|
||||
if ( omnvar_name == "ui_exo_battery_toggle" )
|
||||
{
|
||||
if ( omnvar_value == 1 )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_iconA", weapon_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( self GetLethalWeapon() == weapon_name )
|
||||
{
|
||||
self SetClientOmnvar( omnvar_name + "1", omnvar_value );
|
||||
|
||||
if ( omnvar_name == "ui_exo_battery_toggle" )
|
||||
{
|
||||
if ( omnvar_value == 1 )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_iconB", weapon_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The player doesn't have an Exo Ability - toggle all battery HUD off.
|
||||
self SetClientOmnvar( "ui_exo_battery_iconA", "reset" );
|
||||
self SetClientOmnvar( "ui_exo_battery_iconB", "reset" );
|
||||
self SetClientOmnvar( "ui_exo_battery_toggle0", 0 );
|
||||
self SetClientOmnvar( "ui_exo_battery_toggle1", 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: get_exo_ability_hud_omnvar_value( <weapon_name> , <omnvar_name//self=player> , <returnsstring> )"
|
||||
"Summary: returns the appropriate omnvar value for the Exo Ability corresponding to weapon_name. Returns -1 if the user doesn't have an Exo Ability."
|
||||
"Module: Entity"
|
||||
"CallOn: a player"
|
||||
"MandatoryArg: <weapon_name>: must be a valid Exo Ability (see _utility::is_exo_ability_weapon())"
|
||||
"MandatoryArg: <omnvar_name>: must be a valid omnvar in omnvars.csv (e.g. ui_exo_battery_toggle, ui_exo_battery_level, exo_ability_nrg_req, or exo_ability_nrg_total)."
|
||||
"Example: omnvar = player get_exo_ability_hud_omnvar_value( weapon_name, "ui_exo_battery_toggle" );"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
get_exo_ability_hud_omnvar_value( weapon_name, omnvar_name )
|
||||
{
|
||||
Assert( is_exo_ability_weapon( weapon_name ) );
|
||||
|
||||
if ( self GetTacticalWeapon() == weapon_name )
|
||||
{
|
||||
return self GetClientOmnvar( omnvar_name + "0" );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == weapon_name )
|
||||
{
|
||||
return self GetClientOmnvar( omnvar_name + "1" );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
play_insufficient_tactical_energy_sfx() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "kill_battery" );
|
||||
|
||||
// Removing old notifies if they already exist.
|
||||
self NotifyOnPlayerCommandRemove( "tried_left_exo_ability", "+smoke" );
|
||||
|
||||
// Need to wait, otherwise the below notification won't be added properly.
|
||||
wait( 0.05 );
|
||||
|
||||
self NotifyOnPlayerCommand( "tried_left_exo_ability", "+smoke" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "tried_left_exo_ability" );
|
||||
|
||||
weapon_name = self GetTacticalWeapon();
|
||||
|
||||
if ( is_exo_ability_weapon( weapon_name ) )
|
||||
{
|
||||
if ( self BatteryGetCharge( weapon_name ) < BatteryReqToUse( weapon_name ) )
|
||||
{
|
||||
// The player doesn't have enough energy to use the Exo Ability.
|
||||
self PlayLocalSound( "mp_exo_bat_empty" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
play_insufficient_lethal_energy_sfx() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "kill_battery" );
|
||||
|
||||
// Removing old notify if it already exist.
|
||||
self NotifyOnPlayerCommandRemove( "tried_right_exo_ability", "+frag" );
|
||||
|
||||
// Need to wait, otherwise the below notification won't be added properly.
|
||||
wait( 0.05 );
|
||||
|
||||
self NotifyOnPlayerCommand( "tried_right_exo_ability", "+frag" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "tried_right_exo_ability" );
|
||||
|
||||
weapon_name = self GetLethalWeapon();
|
||||
|
||||
if ( is_exo_ability_weapon( weapon_name ) )
|
||||
{
|
||||
if ( self BatteryGetCharge( weapon_name ) < BatteryReqToUse( weapon_name ) )
|
||||
{
|
||||
// The player doesn't have enough energy to use the Exo Ability.
|
||||
self PlayLocalSound( "mp_exo_bat_empty" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
376
raw/maps/mp/_exo_cloak.gsc
Normal file
376
raw/maps/mp/_exo_cloak.gsc
Normal file
@ -0,0 +1,376 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
//==============================================================================
|
||||
give_exo_cloak() // self = player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
cloakWeapon = get_exo_cloak_weapon();
|
||||
|
||||
if ( self HasWeapon( cloakWeapon ) )
|
||||
return;
|
||||
|
||||
self giveWeapon( cloakWeapon );
|
||||
|
||||
self BatterySetDischargeScale( cloakWeapon, 1.0 );
|
||||
|
||||
self.exo_cloak_on = false;
|
||||
self.exo_cloak_off_time = undefined;
|
||||
|
||||
if ( self GetTacticalWeapon() == cloakWeapon )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", self BatteryGetCharge( cloakWeapon ) );
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", BatteryReqToUse( cloakWeapon ) );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", self BatteryGetSize( cloakWeapon ) );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == cloakWeapon )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", self BatteryGetCharge( cloakWeapon ) );
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", BatteryReqToUse( cloakWeapon ) );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", self BatteryGetSize( cloakWeapon ) );
|
||||
}
|
||||
|
||||
if ( !IsDefined( self.exocloak ) )
|
||||
{
|
||||
self.exocloak = SpawnStruct();
|
||||
}
|
||||
|
||||
self.exocloak.costume = [];
|
||||
self.exocloak.costume["viewmodel"] = self GetViewModel();
|
||||
self.exocloak.costume["body"] = self GetModelFromEntity();
|
||||
|
||||
assert( IsDefined( self.exocloak.costume["viewmodel"] ) );
|
||||
assert( IsDefined( self.exocloak.costume["body"] ) );
|
||||
|
||||
self notify( "exo_cloak_reset" ); // clear any old threads that were spawned from a previous iteration of this function... this is required for timing reasons
|
||||
|
||||
self thread wait_for_exocloak_cancel();
|
||||
self thread wait_for_exocloak_pressed();
|
||||
self thread wait_for_player_death();
|
||||
self thread wait_for_game_end();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
wait_for_exocloak_pressed() // should be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "exo_cloak_reset" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "exo_ability_activate", weaponName );
|
||||
|
||||
if ( weaponName == level.cloakWeapon )
|
||||
{
|
||||
if ( !self IsCloaked() )
|
||||
self thread handle_exocloak();
|
||||
else
|
||||
self active_cloaking_disable( true );
|
||||
}
|
||||
else if ( !is_exo_ability_weapon( weaponname ) )
|
||||
{
|
||||
// Non-Exo-Abilities should deactivate cloak.
|
||||
self active_cloaking_disable( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
wait_for_exocloak_cancel() // should be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "exo_cloak_reset" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill_any( "using_remote", "weapon_fired", "melee_fired", "ground_slam", "grenade_fire" );
|
||||
|
||||
self active_cloaking_disable( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
wait_for_player_death() // should be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "exo_cloak_reset" );
|
||||
|
||||
self waittill_any( "death", "faux_spawn", "joined_team" );
|
||||
|
||||
self active_cloaking_disable( true );
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
handle_exocloak() // should be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "exo_cloak_disabled" );
|
||||
self endon( "exo_cloak_reset" );
|
||||
|
||||
if ( self BatteryGetCharge( level.cloakWeapon ) > 0 )
|
||||
{
|
||||
self active_cloaking_enable();
|
||||
|
||||
while ( self BatteryGetCharge( level.cloakWeapon ) > 0 )
|
||||
{
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
self active_cloaking_disable( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
active_cloaking_enable() // self == player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
println( "active_cloaking_enable() called." );
|
||||
|
||||
self.exo_cloak_on = true;
|
||||
self.exo_cloak_off_time = undefined;
|
||||
|
||||
self CloakingEnable();
|
||||
|
||||
self hideAttachmentsWhileCloaked();
|
||||
|
||||
self BatteryDischargeBegin( level.cloakWeapon );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( level.cloakWeapon, "ui_exo_battery_toggle", 1 );
|
||||
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( level.cloakWeapon );
|
||||
|
||||
self snd_message( "mp_exo_cloak_activate" );
|
||||
|
||||
self.pers["numberOfTimesCloakingUsed"]++;
|
||||
|
||||
if ( isdefined ( level.isHorde ) )
|
||||
{
|
||||
wait 2;
|
||||
self.ignoreme = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
active_cloaking_disable( should_play_fx ) // self == player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
if ( !IsDefined( should_play_fx ) )
|
||||
{
|
||||
should_play_fx = true;
|
||||
}
|
||||
|
||||
if ( !self IsCloaked() )
|
||||
return;
|
||||
|
||||
println( "active_cloaking_disable() called." );
|
||||
|
||||
self.exo_cloak_on = false;
|
||||
self.exo_cloak_off_time = GetTime();
|
||||
|
||||
self CloakingDisable();
|
||||
|
||||
self showAttachmentsAfterCloak();
|
||||
|
||||
if ( isdefined ( level.isHorde ) )
|
||||
self.ignoreme = false;
|
||||
|
||||
self BatteryDischargeEnd( level.cloakWeapon );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( level.cloakWeapon, "ui_exo_battery_toggle", 0 );
|
||||
|
||||
if ( should_play_fx )
|
||||
self snd_message( "mp_exo_cloak_deactivate" );
|
||||
|
||||
self notify( "exo_cloak_disabled" );
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Deactivates Exo Cloak and takes it away without the deactivation sound.
|
||||
take_exo_cloak() // self = player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
cloakWeapon = get_exo_cloak_weapon();
|
||||
|
||||
self notify( "kill_battery" );
|
||||
self active_cloaking_disable( false );
|
||||
self takeWeapon( cloakWeapon );
|
||||
self notify( "exo_cloak_reset" );
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
wait_for_game_end() // self = player, should be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_cloak_reset" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
self active_cloaking_disable( true );
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
hideAttachmentsWhileCloaked() // self = player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
// Exo Overclock
|
||||
if ( self HasWeapon( "adrenaline_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.overclock_on ) && self.overclock_on == true )
|
||||
{
|
||||
KillFXOnTag( level.exo_overclock_vfx_le_active, self, "J_Hip_LE" );
|
||||
KillFXOnTag( level.exo_overclock_vfx_ri_active, self, "J_Hip_RI" );
|
||||
}
|
||||
else
|
||||
{
|
||||
KillFXOnTag( level.exo_overclock_vfx_le_inactive, self, "J_Hip_LE" );
|
||||
KillFXOnTag( level.exo_overclock_vfx_ri_inactive, self, "J_Hip_RI" );
|
||||
}
|
||||
}
|
||||
|
||||
// Exo Trophy
|
||||
if ( self HasWeapon( "exorepulsor_equipment_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.repulsorActive ) && self.repulsorActive == true )
|
||||
{
|
||||
KillFXOnTag( level.exo_repulsor_player_vfx_active, self, "TAG_JETPACK" );
|
||||
}
|
||||
else
|
||||
{
|
||||
KillFXOnTag( level.exo_repulsor_player_vfx_inactive, self, "TAG_JETPACK" );
|
||||
}
|
||||
}
|
||||
|
||||
// Exo Ping
|
||||
if ( self HasWeapon( "exoping_equipment_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.exo_ping_on ) && self.exo_ping_on == true )
|
||||
{
|
||||
KillFXOnTag( level.exo_ping_vfx_active, self, "J_SpineUpper" );
|
||||
}
|
||||
else
|
||||
{
|
||||
KillFXOnTag( level.exo_ping_vfx_inactive, self, "J_SpineUpper" );
|
||||
}
|
||||
}
|
||||
|
||||
// Exo Stim
|
||||
if ( self HasWeapon( "extra_health_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.exo_health_on ) && self.exo_health_on == true )
|
||||
{
|
||||
KillFXOnTag( level.exo_health_le_active_vfx, self, "J_Shoulder_LE" );
|
||||
KillFXOnTag( level.exo_health_rt_active_vfx, self, "J_Shoulder_RI" );
|
||||
}
|
||||
else
|
||||
{
|
||||
KillFXOnTag( level.exo_health_le_inactive_vfx, self, "J_Shoulder_LE" );
|
||||
KillFXOnTag( level.exo_health_rt_inactive_vfx, self, "J_Shoulder_RI" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
showAttachmentsAfterCloak() // self = player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
// Exo Overclock
|
||||
if ( self HasWeapon( "adrenaline_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.overclock_on ) && self.overclock_on == true )
|
||||
{
|
||||
PlayFXOnTag( level.exo_overclock_vfx_le_active, self, "J_Hip_LE" );
|
||||
PlayFXOnTag( level.exo_overclock_vfx_ri_active, self, "J_Hip_RI" );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayFXOnTag( level.exo_overclock_vfx_le_inactive, self, "J_Hip_LE" );
|
||||
PlayFXOnTag( level.exo_overclock_vfx_ri_inactive, self, "J_Hip_RI" );
|
||||
}
|
||||
}
|
||||
|
||||
// Exo Trophy
|
||||
if ( self HasWeapon( "exorepulsor_equipment_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.repulsorActive ) && self.repulsorActive == true )
|
||||
{
|
||||
PlayFXOnTag( level.exo_repulsor_player_vfx_active, self, "TAG_JETPACK" );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayFXOnTag( level.exo_repulsor_player_vfx_inactive, self, "TAG_JETPACK" );
|
||||
}
|
||||
}
|
||||
|
||||
// Exo Ping
|
||||
if ( self HasWeapon( "exoping_equipment_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.exo_ping_on ) && self.exo_ping_on == true )
|
||||
{
|
||||
PlayFXOnTag( level.exo_ping_vfx_active, self, "J_SpineUpper" );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayFXOnTag( level.exo_ping_vfx_inactive, self, "J_SpineUpper" );
|
||||
}
|
||||
}
|
||||
|
||||
// Exo Stim
|
||||
if ( self HasWeapon( "extra_health_mp" ) )
|
||||
{
|
||||
if ( IsDefined( self.exo_health_on ) && self.exo_health_on == true )
|
||||
{
|
||||
PlayFXOnTag( level.exo_health_le_active_vfx, self, "J_Shoulder_LE" );
|
||||
PlayFXOnTag( level.exo_health_rt_active_vfx, self, "J_Shoulder_RI" );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayFXOnTag( level.exo_health_le_inactive_vfx, self, "J_Shoulder_LE" );
|
||||
PlayFXOnTag( level.exo_health_rt_inactive_vfx, self, "J_Shoulder_RI" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_exo_cloak_weapon()
|
||||
{
|
||||
if ( IsDefined( level.cloakWeapon ) )
|
||||
return level.cloakWeapon;
|
||||
|
||||
level.cloakWeapon = "exocloak_equipment_mp";
|
||||
if ( isdefined ( level.isHorde ) )
|
||||
level.cloakWeapon = "exocloakhorde_equipment_mp";
|
||||
|
||||
return level.cloakWeapon;
|
||||
}
|
||||
|
176
raw/maps/mp/_exo_hover.gsc
Normal file
176
raw/maps/mp/_exo_hover.gsc
Normal file
@ -0,0 +1,176 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
exo_hover_think() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_hover_taken" );
|
||||
|
||||
if ( !self HasWeapon( level.hoverWeapon ) )
|
||||
return;
|
||||
|
||||
exo_hover_init();
|
||||
|
||||
//battery discharge starts and ends in runtime for hover
|
||||
for( ;; )
|
||||
{
|
||||
if ( self BatteryIsInUse( level.hoverWeapon ) == false )
|
||||
self waittillmatch ( "battery_discharge_begin", level.hoverWeapon );
|
||||
|
||||
self.exo_hover_on = true;
|
||||
|
||||
// Need to handle this here because Exo Hover is not processed like a grenade as other Exo Abilities are.
|
||||
maps\mp\gametypes\_gamelogic::setHasDoneCombat( self, true );
|
||||
|
||||
self.pers["numberOfTimesHoveringUsed"]++;
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( level.hoverWeapon, "ui_exo_battery_toggle", 1 );
|
||||
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( level.hoverWeapon );
|
||||
|
||||
self thread exo_hover_update();
|
||||
self thread play_exo_hover_vfx();
|
||||
|
||||
//Handle SFX
|
||||
if ( self _hasPerk( "specialty_exo_blastsuppressor" ) )
|
||||
{
|
||||
self snd_message( "mp_suppressed_exo_hover" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self snd_message( "mp_regular_exo_hover" );
|
||||
}
|
||||
|
||||
self thread end_exo_hover_on_notifies();
|
||||
|
||||
if ( self BatteryIsInUse( level.hoverWeapon ) == true )
|
||||
self waittillmatch ( "battery_discharge_end", level.hoverWeapon );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( level.hoverWeapon, "ui_exo_battery_toggle", 0 );
|
||||
|
||||
self.exo_hover_on = false;
|
||||
|
||||
self notify( "stop_exo_hover_effects" ); // ending Exo Hover vfx, sfx, rumble
|
||||
}
|
||||
}
|
||||
|
||||
exo_hover_init()
|
||||
{
|
||||
self BatterySetDischargeScale( level.hoverWeapon, 1.0 );
|
||||
maxCharge = self BatteryGetSize( level.hoverWeapon );
|
||||
if ( self GetTacticalWeapon() == level.hoverWeapon )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", maxCharge );
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", maxCharge );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == level.hoverWeapon )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", maxCharge );
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", maxCharge );
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.regular_exo_hover_vfx ) )
|
||||
{
|
||||
level.regular_exo_hover_vfx = LoadFX( "vfx/smoke/exohover_exhaust_continuous" );
|
||||
}
|
||||
if ( !IsDefined( level.suppressed_exo_hover_vfx ) )
|
||||
{
|
||||
level.suppressed_exo_hover_vfx = LoadFX( "vfx/smoke/exohover_exhaust_continuous_suppressed" );
|
||||
}
|
||||
}
|
||||
|
||||
end_exo_hover_on_notifies() // self = player
|
||||
{
|
||||
self endon( "stop_exo_hover_effects" );
|
||||
|
||||
self waittill_any( "death", "disconnect", "joined_team", "faux_spawn", "exo_hover_taken" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( level.hoverWeapon, "ui_exo_battery_toggle", 0 );
|
||||
self.exo_hover_on = false;
|
||||
self notify( "stop_exo_hover_effects" ); // ending Exo Hover vfx, sfx, rumble
|
||||
}
|
||||
|
||||
take_exo_hover() //self = player
|
||||
{
|
||||
level.hoverWeapon = "exohover_equipment_mp";
|
||||
if ( isdefined ( level.isHorde ) )
|
||||
level.hoverWeapon = "exohoverhorde_equipment_mp";
|
||||
|
||||
self notify( "kill_battery" );
|
||||
self notify( "exo_hover_taken" );
|
||||
self takeWeapon( level.hoverWeapon );
|
||||
}
|
||||
|
||||
give_exo_hover() //self = player
|
||||
{
|
||||
level.hoverWeapon = "exohover_equipment_mp";
|
||||
if ( isdefined ( level.isHorde ) )
|
||||
level.hoverWeapon = "exohoverhorde_equipment_mp";
|
||||
|
||||
self giveWeapon( level.hoverWeapon );
|
||||
self thread exo_hover_think();
|
||||
}
|
||||
|
||||
exo_hover_update() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_hover_taken" );
|
||||
self endon( "stop_exo_hover_effects" );
|
||||
|
||||
dischargeRate = self BatteryGetDischargeRate( level.hoverWeapon );
|
||||
maxBattery = self BatteryGetSize( level.hoverWeapon );
|
||||
Assert( dischargeRate > 0 );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self PlayRumbleOnEntity( "damage_heavy" );
|
||||
|
||||
velocity = self GetVelocity();
|
||||
latSpeed = Length2D( velocity );
|
||||
minSpeed = 1.0;
|
||||
|
||||
if ( level.gametype == "horde" )
|
||||
{
|
||||
if ( IsDefined( self.hordeExoBattery ) )
|
||||
minSpeed = 1.0 + ( self.hordeExoBattery * -0.1 );
|
||||
}
|
||||
|
||||
self BatterySetDischargeScale( level.hoverWeapon, max( minSpeed, (maxBattery * latSpeed) / (GetDvarInt( "hover_max_travel_distance", 350 ) * dischargeRate) ) );
|
||||
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
play_exo_hover_vfx() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
hover_type = 0;
|
||||
|
||||
if ( self _hasPerk( "specialty_exo_blastsuppressor" ) )
|
||||
{
|
||||
hover_type = 1;
|
||||
hover_vfx = SpawnLinkedFx( level.suppressed_exo_hover_vfx, self, "tag_jetpack" );
|
||||
}
|
||||
else
|
||||
{
|
||||
hover_vfx = SpawnLinkedFx( level.regular_exo_hover_vfx, self, "tag_jetpack" );
|
||||
}
|
||||
TriggerFX( hover_vfx );
|
||||
|
||||
self waittill_any( "disconnect", "death", "joined_team", "faux_spawn", "exo_hover_taken", "stop_exo_hover_effects" );
|
||||
|
||||
if ( IsDefined( hover_vfx ) )
|
||||
hover_vfx delete();
|
||||
}
|
235
raw/maps/mp/_exo_mute.gsc
Normal file
235
raw/maps/mp/_exo_mute.gsc
Normal file
@ -0,0 +1,235 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\perks\_perkfunctions;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
|
||||
exo_mute_think() // self = player
|
||||
{
|
||||
self notify( "exo_mute_taken" ); // Cleans up existing Exo Mute threads.
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_mute_taken" );
|
||||
|
||||
if ( !self HasWeapon( "exomute_equipment_mp" ) )
|
||||
return;
|
||||
|
||||
self exo_mute_init();
|
||||
|
||||
self thread monitor_player_death();
|
||||
self thread wait_for_game_end();
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "exo_ability_activate", weapon_name );
|
||||
|
||||
if ( weapon_name == "exomute_equipment_mp" )
|
||||
{
|
||||
self thread try_use_exo_mute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exo_mute_init()
|
||||
{
|
||||
self.mute_on = false;
|
||||
|
||||
self BatterySetDischargeScale( "exomute_equipment_mp", 1.0 );
|
||||
full_energy = self BatteryGetSize( "exomute_equipment_mp" );
|
||||
|
||||
if ( self GetTacticalWeapon() == "exomute_equipment_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", full_energy );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == "exomute_equipment_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", full_energy );
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.exo_mute_3p ) )
|
||||
level.exo_mute_3p = LoadFX( "vfx/unique/exo_mute_3p" );
|
||||
// if ( !IsDefined( level.exo_mute_vfx_ri_active ) )
|
||||
// level.exo_mute_vfx_ri_active = LoadFX( "vfx/lights/exo_mute_hip_ri_start" );
|
||||
// if ( !IsDefined( level.exo_mute_vfx_le_inactive ) )
|
||||
// level.exo_mute_vfx_le_inactive = LoadFX( "vfx/lights/exo_mute_hip_le_inactive" );
|
||||
// if ( !IsDefined( level.exo_mute_vfx_ri_inactive ) )
|
||||
// level.exo_mute_vfx_ri_inactive = LoadFX( "vfx/lights/exo_mute_hip_ri_inactive" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !inVirtualLobby() )
|
||||
{
|
||||
//turn on fx for inactive exo mute
|
||||
// PlayFXOnTag( level.exo_mute_vfx_le_inactive, self, "J_Hip_LE" );
|
||||
// PlayFXOnTag( level.exo_mute_vfx_ri_inactive, self, "J_Hip_RI" );
|
||||
}
|
||||
}
|
||||
|
||||
try_use_exo_mute( ) // self == player
|
||||
{
|
||||
self endon( "exo_mute_taken" );
|
||||
|
||||
if ( self.mute_on == true )
|
||||
{
|
||||
self thread stop_exo_mute( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread start_exo_mute();
|
||||
}
|
||||
}
|
||||
|
||||
killMuteFx() // self == player
|
||||
{
|
||||
if ( IsDefined( self.mute_fx ) )
|
||||
{
|
||||
self.mute_fx delete();
|
||||
self.mute_fx = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
start_exo_mute()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_mute_taken" );
|
||||
self endon( "end_exo_mute" );
|
||||
|
||||
self.mute_on = true;
|
||||
|
||||
self givePerk( "specialty_quieter", false );
|
||||
|
||||
self BatteryDischargeBegin( "exomute_equipment_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "exomute_equipment_mp", "ui_exo_battery_toggle", 1 );
|
||||
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( "exomute_equipment_mp" );
|
||||
|
||||
self thread monitor_mute_battery_charge();
|
||||
|
||||
// activation sound
|
||||
self snd_message( "mp_exo_mute_activate" );
|
||||
|
||||
// vfx
|
||||
// KillFXOnTag( level.exo_mute_vfx_le_inactive, self, "J_Hip_LE" );
|
||||
// KillFXOnTag(level.exo_mute_vfx_ri_inactive, self, "J_Hip_RI" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !self.mute_on ) //turned off during wait?
|
||||
return;
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.mute_fx = SpawnLinkedFx( level.exo_mute_3p, self, "TAG_ORIGIN" );
|
||||
TriggerFX( self.mute_fx );
|
||||
// PlayFXOnTag( level.exo_mute_vfx_ri_active, self, "J_Hip_RI" );
|
||||
}
|
||||
}
|
||||
|
||||
stop_exo_mute( should_play_fx ) // self = player
|
||||
{
|
||||
if ( !isDefined( self.mute_on ) || !self.mute_on )
|
||||
return;
|
||||
|
||||
if ( !IsDefined( should_play_fx ) )
|
||||
{
|
||||
should_play_fx = true;
|
||||
}
|
||||
|
||||
self notify( "end_exo_mute" );
|
||||
|
||||
self.mute_on = false;
|
||||
|
||||
self UnSetPerk( "specialty_quieter", true );
|
||||
|
||||
self BatteryDischargeEnd( "exomute_equipment_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "exomute_equipment_mp", "ui_exo_battery_toggle", 0 );
|
||||
|
||||
killMuteFx();
|
||||
// KillFXOnTag( level.exo_mute_vfx_ri_active, self, "J_Hip_RI" );
|
||||
|
||||
if ( should_play_fx == true )
|
||||
{
|
||||
// deactivation sound
|
||||
self snd_message( "mp_exo_mute_deactivate" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
// PlayFXOnTag( level.exo_mute_vfx_le_inactive, self, "J_Hip_LE" );
|
||||
// PlayFXOnTag( level.exo_mute_vfx_ri_inactive, self, "J_Hip_RI" );
|
||||
}
|
||||
}
|
||||
|
||||
//jwells - overlay VFX handled in code so killcam will work
|
||||
}
|
||||
|
||||
monitor_player_death()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self waittill_any( "death", "joined_team", "faux_spawn", "exo_mute_taken" );
|
||||
|
||||
self thread stop_exo_mute( false );
|
||||
}
|
||||
|
||||
monitor_mute_battery_charge() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_mute_taken" );
|
||||
self endon( "end_exo_mute" );
|
||||
|
||||
while ( self.mute_on == true )
|
||||
{
|
||||
if ( self BatteryGetCharge( "exomute_equipment_mp" ) <= 0 )
|
||||
{
|
||||
thread stop_exo_mute( true );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
take_exo_mute() //self = player
|
||||
{
|
||||
self notify( "kill_battery" );
|
||||
self notify( "exo_mute_taken" );
|
||||
self takeWeapon( "exomute_equipment_mp" );
|
||||
}
|
||||
|
||||
give_exo_mute() //self = player
|
||||
{
|
||||
self giveWeapon( "exomute_equipment_mp" );
|
||||
self thread exo_mute_think();
|
||||
}
|
||||
|
||||
wait_for_game_end() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_mute_taken" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
self thread stop_exo_mute( false );
|
||||
}
|
252
raw/maps/mp/_exo_ping.gsc
Normal file
252
raw/maps/mp/_exo_ping.gsc
Normal file
@ -0,0 +1,252 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
EXO_PING_RANGE = 700;
|
||||
EXO_PING_RANGE_SQ = 490000;
|
||||
EXO_PING_DURATION = 1.5;
|
||||
EXO_PING_THREAT_DURATION = 1.75;
|
||||
MIN_TIME_BETWEEN_PINGS = 5000;
|
||||
|
||||
|
||||
exo_ping_think() // self = player
|
||||
{
|
||||
self notify( "exo_ping_taken" ); // Cleans up existing Exo Ping threads.
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_ping_taken" );
|
||||
|
||||
if ( !self HasWeapon( "exoping_equipment_mp" ) )
|
||||
return;
|
||||
|
||||
self exo_ping_init();
|
||||
|
||||
self thread toggle_exo_ping();
|
||||
self thread wait_for_player_death();
|
||||
self thread wait_for_game_end();
|
||||
}
|
||||
|
||||
|
||||
exo_ping_init()
|
||||
{
|
||||
self.exo_ping_on = false;
|
||||
|
||||
self BatterySetDischargeScale( "exoping_equipment_mp", 1.0 );
|
||||
full_energy = self BatteryGetSize( "exoping_equipment_mp" );
|
||||
|
||||
if ( self GetTacticalWeapon() == "exoping_equipment_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", full_energy );
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", BatteryReqToUse( "exoping_equipment_mp" ) );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", full_energy );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == "exoping_equipment_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", full_energy );
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", BatteryReqToUse( "exoping_equipment_mp" ) );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", full_energy );
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.exo_ping_vfx_inactive ) )
|
||||
{
|
||||
level.exo_ping_vfx_inactive = LoadFX( "vfx/unique/exo_ping_inactive" );
|
||||
}
|
||||
if ( !IsDefined( level.exo_ping_vfx_active ) )
|
||||
{
|
||||
level.exo_ping_vfx_active = LoadFX( "vfx/unique/exo_ping_active" );
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
|
||||
if ( !inVirtualLobby() )
|
||||
{
|
||||
PlayFXOnTag( level.exo_ping_vfx_inactive, self, "J_SpineUpper" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toggle_exo_ping() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_ping_taken" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "exo_ability_activate", weapname );
|
||||
if ( weapname != "exoping_equipment_mp" )
|
||||
continue;
|
||||
|
||||
if ( self.exo_ping_on == true )
|
||||
{
|
||||
self thread stop_exo_ping();
|
||||
}
|
||||
else if ( self HasWeapon( "exoping_equipment_mp" ) )
|
||||
{
|
||||
if ( self BatteryGetCharge( "exoping_equipment_mp" ) > 0 )
|
||||
{
|
||||
self start_exo_ping();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
monitor_exoping_battery_charge() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_ping_taken" );
|
||||
self endon( "stop_exo_ping" );
|
||||
|
||||
while ( self.exo_ping_on == true )
|
||||
{
|
||||
if ( self BatteryGetCharge( "exoping_equipment_mp" ) <= 0 )
|
||||
{
|
||||
self thread stop_exo_ping();
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
take_exo_ping() //self = player
|
||||
{
|
||||
self notify( "kill_battery" );
|
||||
self notify( "exo_ping_taken" );
|
||||
self takeWeapon( "exoping_equipment_mp" );
|
||||
}
|
||||
|
||||
|
||||
give_exo_ping() //self = player
|
||||
{
|
||||
self giveWeapon( "exoping_equipment_mp" );
|
||||
self thread exo_ping_think();
|
||||
}
|
||||
|
||||
killPingFx() //self = player
|
||||
{
|
||||
if ( IsDefined( self.ping_fx ) )
|
||||
{
|
||||
self.ping_fx delete();
|
||||
self.ping_fx = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
start_exo_ping() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_ping_taken" );
|
||||
self endon( "stop_exo_ping" );
|
||||
|
||||
self.exo_ping_on = true;
|
||||
|
||||
self.highlight_effect = maps\mp\_threatdetection::detection_highlight_hud_effect_on( self, -1 );
|
||||
|
||||
self SetPerk( "specialty_exo_ping", true, false );
|
||||
|
||||
self BatteryDischargeOnce( "exoping_equipment_mp", BatteryUsePerShot( "exoping_equipment_mp" ) );
|
||||
self BatteryDischargeBegin( "exoping_equipment_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "exoping_equipment_mp", "ui_exo_battery_toggle", 1 );
|
||||
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( "exoping_equipment_mp" );
|
||||
|
||||
self thread monitor_exoping_battery_charge();
|
||||
|
||||
// activation sound
|
||||
self snd_message( "mp_exo_ping_activate" );
|
||||
|
||||
// vfx
|
||||
killPingFx();
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.ping_fx = SpawnLinkedFx( level.exo_ping_vfx_active, self, "J_SpineUpper" );
|
||||
TriggerFX( self.ping_fx );
|
||||
}
|
||||
|
||||
//jwells - overlay VFX handled in code so killcam will work
|
||||
}
|
||||
|
||||
|
||||
stop_exo_ping( should_play_fx ) // self = player
|
||||
{
|
||||
if ( !isDefined( self.exo_ping_on ) || !self.exo_ping_on )
|
||||
return;
|
||||
|
||||
if ( !IsDefined( should_play_fx ) )
|
||||
{
|
||||
should_play_fx = true;
|
||||
}
|
||||
|
||||
self notify( "stop_exo_ping" );
|
||||
|
||||
self.exo_ping_on = false;
|
||||
|
||||
if ( IsDefined( self.highlight_effect ) )
|
||||
maps\mp\_threatdetection::detection_highlight_hud_effect_off( self.highlight_effect );
|
||||
|
||||
self UnSetPerk( "specialty_exo_ping", true );
|
||||
|
||||
self BatteryDischargeEnd( "exoping_equipment_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "exoping_equipment_mp", "ui_exo_battery_toggle", 0 );
|
||||
|
||||
killPingFx();
|
||||
|
||||
if ( should_play_fx == true )
|
||||
{
|
||||
// deactivation sound
|
||||
self snd_message( "mp_exo_ping_deactivate" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.ping_fx = SpawnLinkedFx( level.exo_ping_vfx_inactive, self, "J_SpineUpper" );
|
||||
TriggerFX( self.ping_fx );
|
||||
}
|
||||
}
|
||||
|
||||
//jwells - overlay VFX handled in code so killcam will work
|
||||
}
|
||||
|
||||
|
||||
// Deactivates and takes away the Exo Ping without deactivation sound or inactive VFX.
|
||||
wait_for_player_death() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self waittill_any( "death", "joined_team", "faux_spawn", "exo_ping_taken" );
|
||||
|
||||
self thread stop_exo_ping( false );
|
||||
}
|
||||
|
||||
|
||||
wait_for_game_end() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_ping_taken" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
self thread stop_exo_ping( false );
|
||||
}
|
486
raw/maps/mp/_exo_repulsor.gsc
Normal file
486
raw/maps/mp/_exo_repulsor.gsc
Normal file
@ -0,0 +1,486 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
EXO_REPULSOR_RADIUS = 385;
|
||||
EXO_REPULSOR_REPEL_VEL = 800;
|
||||
|
||||
exo_repulsor_think() //self = player
|
||||
{
|
||||
self notify( "exo_repulsor_taken" ); // Cleans up existing Exo Trophy System threads.
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_repulsor_taken" );
|
||||
|
||||
if ( !self HasWeapon( "exorepulsor_equipment_mp" ) )
|
||||
return;
|
||||
|
||||
exoRepulsorInit();
|
||||
|
||||
self thread MonitorPlayerDeath();
|
||||
self thread wait_for_game_end();
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "exo_ability_activate", weapname );
|
||||
|
||||
if ( weapname == "exorepulsor_equipment_mp" )
|
||||
{
|
||||
self thread tryUseRepulsor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exoRepulsorInit() //self = player
|
||||
{
|
||||
self.repulsorActive = false;
|
||||
|
||||
self BatterySetDischargeScale( "exorepulsor_equipment_mp", 1.0 );
|
||||
full_energy = self BatteryGetSize( "exorepulsor_equipment_mp" );
|
||||
self.projectilesStopped = 0;
|
||||
|
||||
if ( self GetTacticalWeapon() == "exorepulsor_equipment_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", full_energy );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == "exorepulsor_equipment_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", full_energy );
|
||||
}
|
||||
|
||||
// VFX
|
||||
if ( !IsDefined( level.exo_repulsor_impact ) )
|
||||
{
|
||||
level.exo_repulsor_impact = LoadFX( "vfx/explosion/exo_repulsor_impact" );
|
||||
}
|
||||
if ( !IsDefined( level.exo_repulsor_activate_vfx ) )
|
||||
{
|
||||
level.exo_repulsor_activate_vfx = LoadFX( "vfx/unique/repulsor_bubble" );
|
||||
}
|
||||
if ( !IsDefined( level.exo_repulsor_deactivate_vfx ) )
|
||||
{
|
||||
level.exo_repulsor_deactivate_vfx = LoadFX( "vfx/unique/repulsor_bubble_deactivate" );
|
||||
}
|
||||
if ( !IsDefined( level.exo_repulsor_player_vfx_active ) )
|
||||
{
|
||||
level.exo_repulsor_player_vfx_active = LoadFX( "vfx/unique/exo_repulsor_emitter" );
|
||||
}
|
||||
if ( !IsDefined( level.exo_repulsor_player_vfx_inactive ) )
|
||||
{
|
||||
level.exo_repulsor_player_vfx_inactive = LoadFX( "vfx/unique/exo_repulsor_inactive" );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !inVirtualLobby() )
|
||||
{
|
||||
PlayFXOnTag( level.exo_repulsor_player_vfx_inactive, self, "TAG_JETPACK" );
|
||||
}
|
||||
}
|
||||
|
||||
tryUseRepulsor( duration )
|
||||
{
|
||||
self endon( "exo_repulsor_taken" );
|
||||
|
||||
if (self.repulsorActive == true)
|
||||
{
|
||||
self thread stop_repulsor( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread start_repulsor();
|
||||
}
|
||||
}
|
||||
|
||||
killRepulsorFx()
|
||||
{
|
||||
if ( IsDefined( self.repulsor_fx ) )
|
||||
{
|
||||
self.repulsor_fx delete();
|
||||
self.repulsor_fx = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
start_repulsor()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "stop_exo_repulsor" );
|
||||
self endon( "exo_repulsor_taken" );
|
||||
|
||||
self.repulsorActive = true;
|
||||
|
||||
self thread do_exo_repulsor();
|
||||
|
||||
self BatteryDischargeBegin( "exorepulsor_equipment_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "exorepulsor_equipment_mp", "ui_exo_battery_toggle", 1 );
|
||||
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( "exorepulsor_equipment_mp" );
|
||||
|
||||
// play sfx
|
||||
self snd_message( "mp_exo_repulsor_activate" );
|
||||
|
||||
// play vfx
|
||||
killRepulsorFx();
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.repulsor_fx = SpawnLinkedFx( level.exo_repulsor_player_vfx_active, self, "TAG_JETPACK" );
|
||||
TriggerFX( self.repulsor_fx );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !self.repulsorActive ) //turned off during wait?
|
||||
return;
|
||||
|
||||
if ( IsDefined( level.exo_repulsor_activate_vfx ) )
|
||||
{
|
||||
PlayFXOnTagForClients( level.exo_repulsor_activate_vfx, self, "j_head", self );
|
||||
}
|
||||
}
|
||||
|
||||
stop_repulsor( should_play_fx ) // self = player
|
||||
{
|
||||
if ( !isDefined( self.repulsorActive ) || !self.repulsorActive )
|
||||
return;
|
||||
|
||||
if ( !IsDefined( should_play_fx ) )
|
||||
{
|
||||
should_play_fx = true;
|
||||
}
|
||||
|
||||
self notify( "stop_exo_repulsor" );
|
||||
|
||||
self.repulsorActive = false;
|
||||
|
||||
self BatteryDischargeEnd( "exorepulsor_equipment_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "exorepulsor_equipment_mp", "ui_exo_battery_toggle", 0 );
|
||||
|
||||
// stop vfx
|
||||
killRepulsorFx();
|
||||
|
||||
if ( should_play_fx == true )
|
||||
{
|
||||
// play deactivate sfx
|
||||
self snd_message( "mp_exo_repulsor_deactivate" );
|
||||
|
||||
// play inactive vfx
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.repulsor_fx = SpawnLinkedFx( level.exo_repulsor_player_vfx_inactive, self, "TAG_JETPACK" );
|
||||
TriggerFX( self.repulsor_fx );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
// play deactivate hud vfx
|
||||
if ( IsDefined( level.exo_repulsor_deactivate_vfx ) )
|
||||
{
|
||||
PlayFXOnTagForClients( level.exo_repulsor_deactivate_vfx, self, "j_head", self );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MonitorPlayerDeath()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self waittill_any( "death", "joined_team", "faux_spawn", "exo_repulsor_taken" );
|
||||
|
||||
self.projectilesStopped = 0;
|
||||
|
||||
self thread stop_repulsor( false );
|
||||
}
|
||||
|
||||
update_exo_battery_hud()
|
||||
{
|
||||
battery_energy = self BatteryGetCharge( "exorepulsor_equipment_mp" );
|
||||
// Setting ui_exo_battery_level to the current energy level.
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "exorepulsor_equipment_mp", "ui_exo_battery_level", battery_energy );
|
||||
}
|
||||
|
||||
do_exo_repulsor()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "stop_exo_repulsor" );
|
||||
self endon( "exo_repulsor_taken" );
|
||||
|
||||
while( self BatteryGetCharge( "exorepulsor_equipment_mp" ) > 0 )
|
||||
{
|
||||
for ( i = 0; i < level.grenades.size; i++ )
|
||||
{
|
||||
grenade = level.grenades[i];
|
||||
|
||||
// do not repel grenades without a weaponName.
|
||||
if ( !IsDefined( grenade.weaponName ) )
|
||||
continue;
|
||||
|
||||
// do not repel Exo Abilities.
|
||||
if ( IsDefined( grenade.weaponName ) && is_exo_ability_weapon( grenade.weaponName ) )
|
||||
continue;
|
||||
|
||||
// don't repel grenades with no owner
|
||||
if ( !IsDefined( grenade.owner ) )
|
||||
continue;
|
||||
|
||||
// don't repel your own grenades
|
||||
if ( IsDefined( grenade.owner ) && grenade.owner == self )
|
||||
continue;
|
||||
|
||||
// don't repel teammate grenades
|
||||
if ( level.teamBased && Isdefined( grenade.owner.team ) && grenade.owner.team == self.team )
|
||||
continue;
|
||||
|
||||
player_to_grenade_dist = Distance( grenade.origin, self.origin );
|
||||
|
||||
if ( player_to_grenade_dist < EXO_REPULSOR_RADIUS )
|
||||
{
|
||||
if ( SightTracePassed( self GetEye(), grenade.origin, false, self ) )
|
||||
{
|
||||
// play vfx
|
||||
player_to_grenade_vector = grenade.origin - self.origin;
|
||||
player_to_grenade_angles = VectorToAngles( player_to_grenade_vector );
|
||||
player_to_grenade_up = AnglesToUp( player_to_grenade_angles );
|
||||
player_to_grenade_forward = AnglesToForward( player_to_grenade_angles );
|
||||
player_to_grenade_forward_norm = VectorNormalize( player_to_grenade_forward );
|
||||
fx_origin = grenade.origin - ( 0.2 * player_to_grenade_dist * player_to_grenade_forward_norm );
|
||||
PlayFX( level.exo_repulsor_impact, fx_origin, player_to_grenade_forward_norm, player_to_grenade_up );
|
||||
|
||||
// play sfx
|
||||
grenade snd_message( "mp_exo_repulsor_repel" );
|
||||
|
||||
// explosive drone special case to remove "head" of the grenade
|
||||
if ( grenade.weaponname == "explosive_drone_mp" )
|
||||
{
|
||||
grenade notify( "mp_exo_repulsor_repel" );
|
||||
|
||||
grenade thread maps\mp\_explosive_drone::explosiveGrenadeDeath();
|
||||
}
|
||||
else
|
||||
{
|
||||
grenade delete();
|
||||
}
|
||||
self.projectilesStopped++;
|
||||
|
||||
//Trophy - destroy a projectile
|
||||
self maps\mp\gametypes\_missions::processChallenge("ch_exoability_repulser");
|
||||
|
||||
self BatteryDischargeOnce( "exorepulsor_equipment_mp", int( self BatteryGetSize( "exorepulsor_equipment_mp" ) / 2 ) );
|
||||
self update_exo_battery_hud();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < level.missiles.size; i++ )
|
||||
{
|
||||
rocket = level.missiles[i];
|
||||
|
||||
// do not repel rockets with no owner
|
||||
if ( !IsDefined( rocket.owner ) )
|
||||
continue;
|
||||
|
||||
// don't repel your own rockets
|
||||
if ( IsDefined( rocket.owner ) && rocket.owner == self )
|
||||
continue;
|
||||
|
||||
// don't repel teammate rockets
|
||||
if ( level.teamBased && Isdefined( rocket.owner.team ) && rocket.owner.team == self.team )
|
||||
continue;
|
||||
|
||||
player_to_rocket_dist = Distance( rocket.origin, self.origin );
|
||||
|
||||
if ( player_to_rocket_dist < EXO_REPULSOR_RADIUS )
|
||||
{
|
||||
if ( SightTracePassed( self GetEye(), rocket.origin, false, self ) )
|
||||
{
|
||||
// play vfx
|
||||
player_to_rocket_vector = rocket.origin - self.origin;
|
||||
player_to_rocket_angles = VectorToAngles( player_to_rocket_vector );
|
||||
player_to_rocket_up = AnglesToUp( player_to_rocket_angles );
|
||||
player_to_rocket_forward = AnglesToForward( player_to_rocket_angles );
|
||||
player_to_rocket_forward_norm = VectorNormalize( player_to_rocket_forward );
|
||||
fx_origin = rocket.origin - ( 0.2 * player_to_rocket_dist * player_to_rocket_forward_norm );
|
||||
PlayFX( level.exo_repulsor_impact, fx_origin, player_to_rocket_forward_norm, player_to_rocket_up );
|
||||
|
||||
// play sfx
|
||||
rocket snd_message( "mp_exo_repulsor_repel" );
|
||||
|
||||
// cleanup tag vfx
|
||||
if ( IsDefined( rocket.weaponname ) && rocket.weaponname == "iw5_exocrossbow_mp" )
|
||||
{
|
||||
StopFXOnTag( getfx( "exocrossbow_sticky_blinking" ), rocket.fx_origin, "tag_origin" );
|
||||
}
|
||||
|
||||
rocket delete();
|
||||
self.projectilesStopped++;
|
||||
|
||||
//Trophy - destroy a projectile
|
||||
self maps\mp\gametypes\_missions::processChallenge("ch_exoability_repulser");
|
||||
|
||||
self BatteryDischargeOnce( "exorepulsor_equipment_mp", int( self BatteryGetSize( "exorepulsor_equipment_mp" ) / 2 ) );
|
||||
self update_exo_battery_hud();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < level.explosivedrones.size; i++ )
|
||||
{
|
||||
explosiveDrone = level.explosivedrones[i];
|
||||
|
||||
if( IsDefined(explosiveDrone) ) // just in case clean up timing is off
|
||||
{
|
||||
// do not repel explosive drones with no owner
|
||||
if ( !IsDefined( explosiveDrone.owner ) )
|
||||
continue;
|
||||
|
||||
// don't repel your own explosive drones
|
||||
if ( IsDefined( explosiveDrone.owner ) && explosiveDrone.owner == self )
|
||||
continue;
|
||||
|
||||
// don't repel teammate explosive drones
|
||||
if ( level.teamBased && Isdefined( explosiveDrone.owner.team ) && explosiveDrone.owner.team == self.team )
|
||||
continue;
|
||||
|
||||
player_to_explosiveDrone_dist = Distance( explosiveDrone.origin, self.origin );
|
||||
|
||||
if ( player_to_explosiveDrone_dist < EXO_REPULSOR_RADIUS )
|
||||
{
|
||||
if ( SightTracePassed( self GetEye(), explosiveDrone.origin, false, self ) )
|
||||
{
|
||||
// play vfx
|
||||
player_to_explosiveDrone_vector = explosiveDrone.origin - self.origin;
|
||||
player_to_explosiveDrone_angles = VectorToAngles( player_to_explosiveDrone_vector );
|
||||
player_to_explosiveDrone_up = AnglesToUp( player_to_explosiveDrone_angles );
|
||||
player_to_explosiveDrone_forward = AnglesToForward( player_to_explosiveDrone_angles );
|
||||
player_to_explosiveDrone_forward_norm = VectorNormalize( player_to_explosiveDrone_forward );
|
||||
fx_origin = explosiveDrone.origin - ( 0.2 * player_to_explosiveDrone_dist * player_to_explosiveDrone_forward_norm );
|
||||
PlayFX( level.exo_repulsor_impact, fx_origin, player_to_explosiveDrone_forward_norm, player_to_explosiveDrone_up );
|
||||
|
||||
// play sfx
|
||||
explosiveDrone snd_message( "mp_exo_repulsor_repel" );
|
||||
|
||||
if (IsDefined (explosiveDrone.explosiveDrone) )
|
||||
{
|
||||
explosiveDrone.explosiveDrone delete();
|
||||
}
|
||||
|
||||
explosiveDrone delete();
|
||||
self.projectilesStopped++;
|
||||
|
||||
//Trophy - destroy a projectile
|
||||
self maps\mp\gametypes\_missions::processChallenge("ch_exoability_repulser");
|
||||
|
||||
self BatteryDischargeOnce( "exorepulsor_equipment_mp", int( self BatteryGetSize( "exorepulsor_equipment_mp" ) / 2 ) );
|
||||
self update_exo_battery_hud();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( tdrone in level.trackingDrones )
|
||||
{
|
||||
// don't destroy tracking drones with no owner
|
||||
if ( !IsDefined( tdrone.owner ) )
|
||||
continue;
|
||||
|
||||
// don't destroy your own tracking drones
|
||||
if ( IsDefined( tdrone.owner ) && tdrone.owner == self )
|
||||
continue;
|
||||
|
||||
// don't destroy teammate tracking drones
|
||||
if ( level.teamBased && Isdefined( tdrone.owner.team ) && tdrone.owner.team == self.team )
|
||||
continue;
|
||||
|
||||
player_to_tdrone_dist = Distance( tdrone.origin, self.origin );
|
||||
|
||||
if ( player_to_tdrone_dist < EXO_REPULSOR_RADIUS )
|
||||
{
|
||||
if ( SightTracePassed( self GetEye(), tdrone.origin, false, self ) )
|
||||
{
|
||||
// play vfx
|
||||
player_to_tdrone_vector = tdrone.origin - self.origin;
|
||||
player_to_tdrone_angles = VectorToAngles( player_to_tdrone_vector );
|
||||
player_to_tdrone_up = AnglesToUp( player_to_tdrone_angles );
|
||||
player_to_tdrone_forward = AnglesToForward( player_to_tdrone_angles );
|
||||
player_to_tdrone_forward_norm = VectorNormalize( player_to_tdrone_forward );
|
||||
fx_origin = tdrone.origin - ( 0.2 * player_to_tdrone_dist * player_to_tdrone_forward_norm );
|
||||
PlayFX( level.exo_repulsor_impact, fx_origin, player_to_tdrone_forward_norm, player_to_tdrone_up );
|
||||
|
||||
// play sfx
|
||||
tdrone snd_message( "mp_exo_repulsor_repel" );
|
||||
|
||||
if ( !IsRemovedEntity( tdrone ) && IsDefined( tdrone ) )
|
||||
{
|
||||
tdrone notify( "death" );
|
||||
decrementFauxVehicleCount();
|
||||
}
|
||||
|
||||
self.projectilesStopped++;
|
||||
|
||||
//Trophy - destroy a projectile
|
||||
self maps\mp\gametypes\_missions::processChallenge("ch_exoability_repulser");
|
||||
|
||||
self BatteryDischargeOnce( "exorepulsor_equipment_mp", int( self BatteryGetSize( "exorepulsor_equipment_mp" ) / 2 ) );
|
||||
self update_exo_battery_hud();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The medal used to be "Four Play" when you could block 4 enemy projectiles. Now that you can only block 2, we've changed it to "Secretary of Defense".
|
||||
if ( self.projectilesStopped >= 2 )
|
||||
{
|
||||
if ( !isdefined ( level.isHorde ) )
|
||||
self thread maps\mp\_events::fourPlayEvent();
|
||||
self.projectilesStopped -= 2;
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
// Exo Repulsor is out of energy.
|
||||
self thread stop_repulsor( true );
|
||||
}
|
||||
|
||||
take_exo_repulsor() //self = player
|
||||
{
|
||||
self notify( "kill_battery" );
|
||||
self notify( "exo_repulsor_taken" );
|
||||
self takeWeapon( "exorepulsor_equipment_mp" );
|
||||
}
|
||||
|
||||
give_exo_repulsor() //self = player
|
||||
{
|
||||
self giveWeapon( "exorepulsor_equipment_mp" );
|
||||
self thread exo_repulsor_think();
|
||||
}
|
||||
|
||||
wait_for_game_end() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_repulsor_taken" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
self thread stop_repulsor( false );
|
||||
}
|
102
raw/maps/mp/_exo_shield.gsc
Normal file
102
raw/maps/mp/_exo_shield.gsc
Normal file
@ -0,0 +1,102 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
exo_shield_think() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_shield_taken" );
|
||||
self notify( "exo_shield_think_end" );
|
||||
self endon( "exo_shield_think_end" );
|
||||
|
||||
exo_shield_weapon = get_exo_shield_weapon();
|
||||
|
||||
if ( !self HasWeapon( exo_shield_weapon ) )
|
||||
return;
|
||||
|
||||
self BatterySetDischargeScale( exo_shield_weapon, 1.0 );
|
||||
|
||||
reqCharge = BatteryUsePerShot( exo_shield_weapon );
|
||||
maxCharge = self BatteryGetSize( exo_shield_weapon );
|
||||
|
||||
if ( self GetTacticalWeapon() == exo_shield_weapon )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", maxCharge );
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", reqCharge );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", maxCharge );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == exo_shield_weapon )
|
||||
{
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", maxCharge );
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", reqCharge );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", maxCharge );
|
||||
}
|
||||
|
||||
self thread wait_for_player_death( exo_shield_weapon );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self waittillmatch( "grenade_pullback", exo_shield_weapon );
|
||||
|
||||
self snd_message( "mp_exo_shield_activate" );
|
||||
|
||||
self.pers["numberOfTimesShieldUsed"]++;
|
||||
|
||||
// NOTE: Don't stow riotshield on the back
|
||||
|
||||
//battery discharge happens in code for shield
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( exo_shield_weapon, "ui_exo_battery_toggle", 1 );
|
||||
self.exo_shield_on = true;
|
||||
|
||||
if( !IsAgent ( self ) )
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( exo_shield_weapon );
|
||||
|
||||
if ( self BatteryIsInUse( exo_shield_weapon ) == true )
|
||||
self waittillmatch( "battery_discharge_end", exo_shield_weapon );
|
||||
|
||||
self snd_message( "mp_exo_shield_deactivate" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( exo_shield_weapon, "ui_exo_battery_toggle", 0 );
|
||||
self.exo_shield_on = false;
|
||||
}
|
||||
}
|
||||
|
||||
take_exo_shield() //self = player
|
||||
{
|
||||
self notify( "kill_battery" );
|
||||
self notify( "exo_shield_taken" );
|
||||
self takeWeapon( get_exo_shield_weapon() );
|
||||
}
|
||||
|
||||
give_exo_shield() //self = player
|
||||
{
|
||||
self giveWeapon( get_exo_shield_weapon() );
|
||||
self thread exo_shield_think();
|
||||
}
|
||||
|
||||
get_exo_shield_weapon()
|
||||
{
|
||||
if ( IsDefined( level.exoShieldweapon ) )
|
||||
return level.exoShieldweapon;
|
||||
|
||||
level.exoShieldweapon = "exoshield_equipment_mp";
|
||||
if ( isdefined ( level.isHorde ) )
|
||||
level.exoShieldweapon = "exoshieldhorde_equipment_mp";
|
||||
|
||||
return level.exoShieldweapon;
|
||||
}
|
||||
|
||||
wait_for_player_death( weapon_name ) // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self waittill_any( "death", "joined_team", "faux_spawn", "exo_shield_taken" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( weapon_name, "ui_exo_battery_toggle", 0 );
|
||||
self.exo_shield_on = false;
|
||||
}
|
204
raw/maps/mp/_exo_suit.gsc
Normal file
204
raw/maps/mp/_exo_suit.gsc
Normal file
@ -0,0 +1,204 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
getGroundSlamMinHeight()
|
||||
{
|
||||
return 120;
|
||||
}
|
||||
|
||||
getGroundSlamMaxHeight()
|
||||
{
|
||||
return 380;
|
||||
}
|
||||
|
||||
getGroundSlamMinDamage()
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
|
||||
getGroundSlamMaxDamage()
|
||||
{
|
||||
return 110;
|
||||
}
|
||||
|
||||
getGroundSlamMinRadius()
|
||||
{
|
||||
return 75;
|
||||
}
|
||||
|
||||
getGroundSlamMaxRadius()
|
||||
{
|
||||
return 125;
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
/#
|
||||
SetDevDvarIfUninitialized("ground_slam_min_height", getGroundSlamMinHeight());
|
||||
SetDevDvarIfUninitialized("ground_slam_max_height", getGroundSlamMaxHeight());
|
||||
SetDevDvarIfUninitialized("ground_slam_min_damage", getGroundSlamMinDamage());
|
||||
SetDevDvarIfUninitialized("ground_slam_max_damage", getGroundSlamMaxDamage());
|
||||
SetDevDvarIfUninitialized("ground_slam_min_radius", getGroundSlamMinRadius());
|
||||
SetDevDvarIfUninitialized("ground_slam_max_radius", getGroundSlamMaxRadius());
|
||||
#/
|
||||
|
||||
//level._effect[ "exo_slam_fx" ] = LoadFX( "vfx/smoke/exo_slam_impact" );
|
||||
//level._effect[ "exo_slam_boots_fx" ] = LoadFX( "vfx/smoke/exo_slam_boots_impact" );
|
||||
level._effect[ "exo_slam_kneeslide_fx" ] = LoadFX( "vfx/code/slam_jetpack_kneeslide" );
|
||||
level thread onPlayerConnect();
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
|
||||
player thread monitorGroundSlam();
|
||||
player thread monitorGroundSlamHitPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
monitorGroundSlam() //self ==player
|
||||
//=========================================================
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
time = 10;
|
||||
origin_size = 4;
|
||||
forward = (1,0,0);
|
||||
right = (0,1,0);
|
||||
up = (0,0,1);
|
||||
|
||||
bounding_box_radius_offset = 16;
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill("ground_slam", height);
|
||||
|
||||
if ( IsDefined( level.groundSlam ) && self [[level.groundSlam]]( height ) )
|
||||
continue;
|
||||
|
||||
min_height = GetDvarFloat("ground_slam_min_height", getGroundSlamMinHeight());
|
||||
max_height = GetDvarFloat("ground_slam_max_height", getGroundSlamMaxHeight());
|
||||
|
||||
min_damage = GetDvarFloat("ground_slam_min_damage", getGroundSlamMinDamage());
|
||||
max_damage = GetDvarFloat("ground_slam_max_damage", getGroundSlamMaxDamage());
|
||||
|
||||
min_radius = GetDvarFloat("ground_slam_min_radius", getGroundSlamMinRadius());
|
||||
max_radius = GetDvarFloat("ground_slam_max_radius", getGroundSlamMaxRadius());
|
||||
|
||||
if( height < min_height )
|
||||
continue;
|
||||
|
||||
scale = (height-min_height) / (max_height-min_height);
|
||||
scale = clamp(scale, 0.0, 1.0);
|
||||
|
||||
radius = ((max_radius-min_radius)*scale) + min_radius;
|
||||
concussion_radius = radius + 60;
|
||||
concussion_radius_sq = concussion_radius * concussion_radius;
|
||||
|
||||
self RadiusDamage(self.origin, radius, max_damage, min_damage, self, "MOD_TRIGGER_HURT", "boost_slam_mp" );
|
||||
|
||||
if ( self _hasPerk( "specialty_exo_slamboots" ) )
|
||||
{
|
||||
// Do concussion VFX and apply concussion effect.
|
||||
//impactFXoffset = 32 * AnglesToForward((0,self.angles[1], 0));
|
||||
//PlayFX( level._effect[ "exo_slam_boots_fx" ], self.origin + impactFXoffset, AnglesToForward( (270,self.angles[1] + 180, 0) ) );
|
||||
//level playSoundinSpace( "stungrenade_explode_default", self.origin );
|
||||
|
||||
//play kneeslide fx
|
||||
PlayFXOnTag( level._effect[ "exo_slam_kneeslide_fx" ], self, "j_knee_ri" );
|
||||
PhysicsExplosionSphere( self.origin, radius, 20, 1 );
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( isReallyAlive( player ) && player != self && ( !level.teamBased || player.team != self.team ) && !player isUsingRemote() )
|
||||
{
|
||||
if ( DistanceSquared( self.origin, player.origin ) < concussion_radius_sq )
|
||||
{
|
||||
player ShellShock( "concussion_grenade_mp", 1.5 );
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_perk_overcharge" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do concussion VFX and apply concussion effect.
|
||||
//impactFXoffset = 32 * AnglesToForward((0,self.angles[1], 0));
|
||||
//PlayFX( level._effect[ "exo_slam_fx" ], self.origin + impactFXoffset, AnglesToForward( (270,self.angles[1] + 180, 0) ) );
|
||||
PhysicsExplosionSphere( self.origin, radius, 20, 0.9 );
|
||||
}
|
||||
|
||||
// level._effect[ "space_explosion" ] = LoadFX( "fx/explosions/space_explosion" );
|
||||
// level._effect[ "space_explosion_small" ] = LoadFX( "fx/explosions/space_explosion_small" );
|
||||
//
|
||||
// slam_fx = level._effect[ "space_explosion_small" ];
|
||||
// if(radius>75)
|
||||
// {
|
||||
// slam_fx = level._effect[ "space_explosion" ];
|
||||
// }
|
||||
//
|
||||
// PlayFX( slam_fx, self.origin );
|
||||
|
||||
if( GetDvarInt("ground_slam_debug") )
|
||||
{
|
||||
thread draw_circle_for_time( self.origin, radius+bounding_box_radius_offset, (0,1,0), false, 16, time ); // Min Damage
|
||||
|
||||
player_health = 100;
|
||||
|
||||
kill_radius = ((player_health-min_damage)*radius)/(max_damage-min_damage);
|
||||
|
||||
thread draw_circle_for_time( self.origin, kill_radius+bounding_box_radius_offset, (1,0,0), false, 16, time ); // Kill damage
|
||||
|
||||
foreach(player in level.players)
|
||||
{
|
||||
line(player.origin, player.origin + (forward*origin_size), forward, 1.0, false, int(time/.05));
|
||||
line(player.origin, player.origin + (right*origin_size), right, 1.0, false, int(time/.05));
|
||||
line(player.origin, player.origin + (up*origin_size), up, 1.0, false, int(time/.05));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw_circle_for_time(center, radius, color, depthTest, segments, time)
|
||||
{
|
||||
loops = time / .05;
|
||||
for(i=0; i<loops; i++)
|
||||
{
|
||||
maps\mp\bots\_bots_util::bot_draw_circle( center, radius, color, depthTest, segments );
|
||||
wait .05;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
monitorGroundSlamHitPlayer() //self ==player
|
||||
//=========================================================
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill("ground_slam_hit_player", victim);
|
||||
|
||||
if ( IsDefined( level.groundSlamHitPlayer ) && self [[level.groundSlamHitPlayer]]( victim ) )
|
||||
continue;
|
||||
|
||||
victim DoDamage( victim.health, self.origin, self, self, "MOD_CRUSH", "boost_slam_mp" );
|
||||
}
|
||||
}
|
||||
|
||||
//========================================================
|
||||
exo_power_cooldown( cooldown_time_sec )
|
||||
//========================================================
|
||||
{
|
||||
cooldown_ms = int(cooldown_time_sec * 1000);
|
||||
|
||||
self SetClientOmnvar("ui_exo_cooldown_time", cooldown_ms);
|
||||
wait cooldown_time_sec;
|
||||
self SetClientOmnvar("ui_exo_cooldown_time", 0);
|
||||
|
||||
self PlayLocalSound("exo_power_recharged");
|
||||
}
|
137
raw/maps/mp/_exocrossbow.gsc
Normal file
137
raw/maps/mp/_exocrossbow.gsc
Normal file
@ -0,0 +1,137 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
CONST_exocrossbow_weaponname = "iw5_exocrossbow";
|
||||
CONST_explosion_delay_time = 1.5;
|
||||
|
||||
monitor_exocrossbow_launch() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
level._effect[ "exocrossbow_sticky_explosion" ] = LoadFx( "vfx/explosion/frag_grenade_default" );
|
||||
level._effect[ "exocrossbow_sticky_blinking" ] = LoadFx( "vfx/lights/light_beacon_crossbow" );
|
||||
|
||||
Assert(IsPlayer(self) || IsAgent(self));
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "missile_fire", projectile, weaponName );
|
||||
|
||||
if( !IsSubStr( weaponName, CONST_exocrossbow_weaponname ) )
|
||||
continue;
|
||||
|
||||
projectile setOtherEnt( self );
|
||||
projectile.ch_crossbow_player_jumping = self IsHighJumping();
|
||||
|
||||
self thread wait_for_stuck( projectile );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wait_for_stuck( projectile ) // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
projectile endon( "death" );
|
||||
|
||||
projectile waittill( "missile_stuck", stuckTo );
|
||||
|
||||
projectile thread determine_sticky_position( self, stuckTo );
|
||||
}
|
||||
|
||||
|
||||
determine_sticky_position( firing_player, stuckTo ) // self = projectile
|
||||
{
|
||||
self endon( "death" );
|
||||
firing_player endon( "disconnect" );
|
||||
firing_player endon( "faux_spawn" );
|
||||
|
||||
if( !isDefined( self ) ) //timed out
|
||||
return;
|
||||
|
||||
if( isDefined( stuckTo ) && !maps\mp\_utility::inVirtualLobby() && isPlayer( stuckTo ) )
|
||||
{
|
||||
self.ch_crossbow_victim_jumping = stuckTo IsHighJumping();
|
||||
|
||||
if( firing_player maps\mp\gametypes\_weapons::isStuckToFriendly(stuckTo) )
|
||||
{
|
||||
self.isStuck = "friendly";
|
||||
}
|
||||
else
|
||||
{
|
||||
self.isStuck = "enemy";
|
||||
self.stuckEnemyEntity = stuckTo;
|
||||
|
||||
firing_player maps\mp\_events::crossbowStickEvent( stuckTo );
|
||||
firing_player notify( "process", "ch_bullseye" );
|
||||
}
|
||||
}
|
||||
|
||||
self thread sticky_timer( firing_player );
|
||||
self thread sticky_fx( firing_player );
|
||||
self thread remove_sticky_on_explosion( firing_player );
|
||||
self thread cleanup_sticky_on_death();
|
||||
self thread maps\mp\gametypes\_weapons::stickyHandleMovers( "detonate" );
|
||||
}
|
||||
|
||||
|
||||
sticky_timer( firing_player ) // self = projectile
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
wait( CONST_explosion_delay_time );
|
||||
|
||||
self notify( "exocrossbow_exploded" );
|
||||
}
|
||||
|
||||
|
||||
sticky_fx( firing_player ) // self = projectile
|
||||
{
|
||||
self endon( "exocrossbow_exploded" );
|
||||
self endon( "death" );
|
||||
|
||||
self.fx_origin = spawn_tag_origin();
|
||||
self.fx_origin.origin = self.origin;
|
||||
self.fx_origin.angles = self.angles;
|
||||
self.fx_origin Show();
|
||||
|
||||
self.fx_origin LinkTo( self );
|
||||
|
||||
wait( 0.1 );
|
||||
|
||||
PlayFXOnTag( getfx( "exocrossbow_sticky_blinking" ), self.fx_origin, "tag_origin" );
|
||||
|
||||
self PlaySound( "exocrossbow_warning" );
|
||||
}
|
||||
|
||||
|
||||
remove_sticky_on_explosion( firing_player )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self waittill( "exocrossbow_exploded" );
|
||||
|
||||
self cleanup_sticky();
|
||||
}
|
||||
|
||||
cleanup_sticky_on_death()
|
||||
{
|
||||
self endon( "exocrossbow_exploded" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self cleanup_sticky();
|
||||
}
|
||||
|
||||
|
||||
cleanup_sticky()
|
||||
{
|
||||
StopFXOnTag( getfx( "exocrossbow_sticky_blinking" ), self.fx_origin, "tag_origin" );
|
||||
self.fx_origin delete();
|
||||
}
|
266
raw/maps/mp/_exoknife.gsc
Normal file
266
raw/maps/mp/_exoknife.gsc
Normal file
@ -0,0 +1,266 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
|
||||
EXO_KNIFE_NAME = "exoknife_mp";
|
||||
EXO_KNIFE_SPEED = 1200.0;
|
||||
EXO_KNIFE_RETURN_DELAY = 0.5;
|
||||
|
||||
init()
|
||||
{
|
||||
SetDevDvarIfUninitialized("exo_knife_speed", EXO_KNIFE_SPEED);
|
||||
SetDevDvarIfUninitialized("exo_knife_return_delay", EXO_KNIFE_RETURN_DELAY);
|
||||
|
||||
level._effect[ "exo_knife_blood" ] = loadfx( "vfx/weaponimpact/flesh_impact_head_fatal_exit" );
|
||||
}
|
||||
|
||||
exo_knife_think()
|
||||
{
|
||||
self thread exo_knife_throw_watch();
|
||||
}
|
||||
|
||||
exo_knife_throw_watch()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill("grenade_fire", knife, weapon_name);
|
||||
|
||||
shortWeaponName = maps\mp\_utility::strip_suffix( weapon_name, "_lefthand" );
|
||||
|
||||
if ( shortWeaponName != EXO_KNIFE_NAME && shortWeaponName != "exoknife_jug_mp" )
|
||||
continue;
|
||||
|
||||
knife.manuallyDetonateFunc = ::exo_knife_manually_detonate;
|
||||
|
||||
knife.flying = true;
|
||||
knife.weaponName = weapon_name;
|
||||
if ( !IsDefined( knife.recall ) )
|
||||
knife.recall = false;
|
||||
knife.owner = self;
|
||||
|
||||
self thread exo_knife_enable_detonate( knife );
|
||||
|
||||
knife thread exo_knife_touch_watch();
|
||||
knife thread exo_knife_stuck_watch();
|
||||
knife thread exo_knife_recall_watch();
|
||||
knife thread exo_knife_emp_watch();
|
||||
}
|
||||
}
|
||||
|
||||
exo_knife_emp_watch()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill( "emp_update" );
|
||||
|
||||
if ( IsDefined( level.empEquipmentDisabled ) && level.empEquipmentDisabled && self.owner isEMPedByKillstreak() )
|
||||
{
|
||||
self thread exo_knife_delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exo_knife_enable_detonate( knife )
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
if ( !IsDefined( self.exoknife_count ) )
|
||||
self.exoknife_count = 0;
|
||||
|
||||
if ( !self.exoknife_count )
|
||||
self _enableDetonate( knife.weaponName, true );
|
||||
|
||||
self.exoknife_count++;
|
||||
|
||||
knife waittill("death");
|
||||
|
||||
self.exoknife_count--;
|
||||
|
||||
if ( !self.exoknife_count )
|
||||
self _enableDetonate( knife.weaponName, false );
|
||||
}
|
||||
|
||||
exo_knife_passed_target()
|
||||
{
|
||||
self endon("death");
|
||||
self.owner endon("disconnect");
|
||||
|
||||
self waittill("missile_passed_target");
|
||||
|
||||
self exo_knife_restock();
|
||||
}
|
||||
|
||||
exo_knife_touch_watch()
|
||||
{
|
||||
if ( !isDefined(self.owner) )
|
||||
return;
|
||||
|
||||
self endon("death");
|
||||
self.owner endon("disconnect");
|
||||
|
||||
trigger = spawn("trigger_radius", self.origin, 0, 15, 5);
|
||||
trigger EnableLinkTo();
|
||||
trigger LinkTo(self);
|
||||
trigger.knife = self;
|
||||
self thread delete_on_death(trigger);
|
||||
|
||||
while(1)
|
||||
{
|
||||
trigger waittill("trigger", player);
|
||||
|
||||
if(player != self.owner)
|
||||
continue;
|
||||
|
||||
if ( player GetFractionMaxAmmo(self.weaponName) >= 1.0 )
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
self exo_knife_restock();
|
||||
}
|
||||
|
||||
exo_knife_restock()
|
||||
{
|
||||
self.owner SetClientOmnvar( "damage_feedback", "throwingknife" );
|
||||
self.owner SetWeaponAmmoStock( self.weaponName, self.owner GetWeaponAmmoStock( self.weaponName ) + 1 );
|
||||
|
||||
self exo_knife_delete();
|
||||
}
|
||||
|
||||
exo_knife_stuck_watch()
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill("missile_stuck", hitEnt);
|
||||
|
||||
isStuckToShield = self maps\mp\_riotshield::entIsStuckToShield();
|
||||
|
||||
self.flying = false;
|
||||
self.recall = false;
|
||||
|
||||
if ( IsDefined( self.owner ) && IsDefined(hitEnt) && ( ( IsDefined( level.isHorde ) && level.isHorde && hitEnt.model == "animal_dobernan" ) || IsGameParticipant(hitEnt) ) && !isStuckToShield )
|
||||
{
|
||||
if( IsDefined( hitEnt.team ) && IsDefined( self.owner.team ) && hitEnt.team != self.owner.team )
|
||||
{
|
||||
//playfx(getfx("exo_knife_blood"), self.origin, (self.origin - self.owner.origin));
|
||||
PlayImpactHeadFatalFX( self.origin, (self.origin - self.owner.origin) );
|
||||
}
|
||||
|
||||
hitEnt maps\mp\_snd_common_mp::snd_message( "exo_knife_player_impact" );
|
||||
|
||||
return_delay = GetDvarFloat("exo_knife_return_delay", EXO_KNIFE_RETURN_DELAY);
|
||||
self.owner thread exo_knife_recall(return_delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread maps\mp\gametypes\_weapons::stickyHandleMovers( undefined, "exo_knife_recall" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exo_knife_recall_stuck_watch()
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
while (1)
|
||||
{
|
||||
self waittill("missile_stuck", hitEnt);
|
||||
|
||||
if ( IsDefined( self.owner ) && IsDefined( hitEnt ) && ( hitEnt IsJuggernaut() ) )
|
||||
{
|
||||
if ( !level.teambased || ( IsDefined( self.owner.team ) && IsDefined( hitEnt.team ) && hitEnt.team != self.owner.team ) )
|
||||
{
|
||||
self thread exo_knife_delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exo_knife_recall(waitTime)
|
||||
{
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
self endon("exo_knife_recall");
|
||||
|
||||
if ( IsDefined(waitTime) && waitTime > 0)
|
||||
wait waitTime;
|
||||
|
||||
self notify("exo_knife_recall");
|
||||
}
|
||||
|
||||
exo_knife_manually_detonate(knife)
|
||||
{
|
||||
if ( IsDefined(knife) && !knife.recall )
|
||||
{
|
||||
self exo_knife_recall();
|
||||
}
|
||||
}
|
||||
|
||||
exo_knife_recall_watch()
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
if(!isDefined(self.owner))
|
||||
return;
|
||||
self.owner endon("disconnect");
|
||||
self.owner endon("death");
|
||||
|
||||
self.owner waittill("exo_knife_recall");
|
||||
|
||||
start = self.origin;
|
||||
end = self.owner GetEye();
|
||||
|
||||
if ( self.owner GetStance() != "prone" )
|
||||
{
|
||||
end -= (0,0,20);
|
||||
}
|
||||
|
||||
speed = GetDvarFloat("exo_knife_speed", EXO_KNIFE_SPEED);
|
||||
dist = Distance(start,end);
|
||||
time = dist/speed;
|
||||
|
||||
player_vel = self.owner GetVelocity();
|
||||
end = end + player_vel*time;
|
||||
|
||||
dir = end - start;
|
||||
dir = VectorNormalize(dir);
|
||||
|
||||
return_knife_offset = 0;
|
||||
if ( return_knife_offset != 0 )
|
||||
{
|
||||
start += dir * return_knife_offset;
|
||||
}
|
||||
|
||||
//vel = trajectorycalculateinitialvelocity(start, end, (0,0,-800), time);
|
||||
vel = dir*speed;
|
||||
|
||||
return_knife = MagicGrenadeManual(self.weaponName, start, vel, 30, self.owner, true);
|
||||
return_knife.owner = self.owner;
|
||||
return_knife.recall = true;
|
||||
|
||||
return_knife Missile_SetTargetEnt( self.owner );
|
||||
return_knife thread exo_knife_recall_stuck_watch();
|
||||
return_knife thread exo_knife_passed_target();
|
||||
|
||||
self exo_knife_delete();
|
||||
}
|
||||
|
||||
exo_knife_clean_up_attractor(attractor, owner, knife)
|
||||
{
|
||||
waittill_any_ents(owner, "disconnect", owner, "death", knife, "death", knife, "missile_stuck");
|
||||
Missile_DeleteAttractor(attractor);
|
||||
}
|
||||
|
||||
exo_knife_delete()
|
||||
{
|
||||
self delete();
|
||||
}
|
1975
raw/maps/mp/_explosive_drone.gsc
Normal file
1975
raw/maps/mp/_explosive_drone.gsc
Normal file
File diff suppressed because it is too large
Load Diff
431
raw/maps/mp/_explosive_gel.gsc
Normal file
431
raw/maps/mp/_explosive_gel.gsc
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
Explosive Gel
|
||||
Author: Keith Evans
|
||||
Description: replacement for C4 and Semtex
|
||||
sticky grenade with timed detonation that can be detonated manually while in the air
|
||||
will eventually be merged with the exo_launcher to have a long range charge shot component
|
||||
*/
|
||||
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\perks\_perkfunctions;
|
||||
|
||||
CONST_Mine_DamageRadius = 192;
|
||||
CONST_Mine_DamageMax = 100;
|
||||
CONST_Mine_DamageMin = 100;
|
||||
|
||||
watchExplosiveGelUsage()
|
||||
{
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "grenade_fire", grenade, weapname );
|
||||
if ( weapname == "explosive_gel_mp")
|
||||
{
|
||||
if( !IsAlive( self ) )
|
||||
{
|
||||
grenade delete();
|
||||
return;
|
||||
}
|
||||
|
||||
thread tryUseExplosiveGel( grenade );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
precacheModel( "weapon_c4" );
|
||||
precacheModel( "weapon_c4_bombsquad" );
|
||||
|
||||
level.explosiveGelSettings = SpawnStruct();
|
||||
level.explosiveGelSettings.stuckMesh = "weapon_c4";
|
||||
level.explosiveGelSettings.gelBombSquadMesh = "weapon_c4_bombsquad";
|
||||
level.explosiveGelSettings.gelExplosionFx = LoadFX( "vfx/explosion/frag_grenade_default" );
|
||||
level.explosiveGelSettings.beacon[ "enemy" ] = loadfx( "vfx/lights/light_c4_blink" );
|
||||
level.explosiveGelSettings.beacon[ "friendly" ] = loadfx( "vfx/lights/light_mine_blink_friendly" );
|
||||
}
|
||||
|
||||
tryUseExplosiveGel( grenade ) // self == player
|
||||
{
|
||||
thread LaunchExplosiveGel( grenade );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LaunchExplosiveGel( grenade )
|
||||
{
|
||||
thread watchExplosiveGelAltDetonate( grenade );
|
||||
Mine = self StickExplosiveGel( grenade ) ;
|
||||
}
|
||||
|
||||
|
||||
watchExplosiveGelAltDetonate( grenade )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "change_owner" );
|
||||
grenade endon( "missile_stuck" );
|
||||
grenade endon( "death" );
|
||||
|
||||
buttonTime = 0;
|
||||
for ( ;; )
|
||||
{
|
||||
if ( self UseButtonPressed() )
|
||||
{
|
||||
buttonTime = 0;
|
||||
while ( self UseButtonPressed() )
|
||||
{
|
||||
buttonTime += 0.05;
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
println( "pressTime1: " + buttonTime );
|
||||
if ( buttonTime >= 0.5 )
|
||||
continue;
|
||||
|
||||
buttonTime = 0;
|
||||
while ( !self UseButtonPressed() && buttonTime < 0.5 )
|
||||
{
|
||||
buttonTime += 0.05;
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
println( "delayTime: " + buttonTime );
|
||||
if ( buttonTime >= 0.5 )
|
||||
continue;
|
||||
|
||||
|
||||
self thread EarlyDetonate( grenade );
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
StickExplosiveGel( grenade )
|
||||
{
|
||||
self endon( "earlyNotify" );
|
||||
grenade waittill( "missile_stuck" );
|
||||
|
||||
Assert( isDefined( self ) );
|
||||
|
||||
tracedown = bulletTrace( grenade.origin, grenade.origin - (0, 0, 4), false, grenade );
|
||||
|
||||
traceup = bulletTrace( grenade.origin, grenade.origin + (0, 0, 4), false, grenade );
|
||||
|
||||
ForwardAngles = AnglesToForward(grenade.angles);
|
||||
|
||||
traceforward = bulletTrace( grenade.origin + (0, 0, 4), (grenade.origin + (ForwardAngles*4)), false, grenade );
|
||||
|
||||
trace = undefined;
|
||||
IsUp = false;
|
||||
IsForward = false;
|
||||
|
||||
if(traceforward["surfacetype"] != "none")
|
||||
{
|
||||
trace = traceforward;
|
||||
IsForward = true;
|
||||
}
|
||||
else if(traceup["surfacetype"] != "none")
|
||||
{
|
||||
trace = traceup;
|
||||
IsUp = true;
|
||||
}
|
||||
else if(tracedown["surfacetype"] != "none")
|
||||
trace = tracedown;
|
||||
else
|
||||
trace = tracedown; //fallback
|
||||
|
||||
SpawnOrigin = trace[ "position" ];
|
||||
|
||||
if(SpawnOrigin == traceup[ "position" ] )
|
||||
{
|
||||
SpawnOrigin+=(0,0,-5);
|
||||
}
|
||||
|
||||
ExplosiveGel = Spawn( "script_model", SpawnOrigin );
|
||||
|
||||
ExplosiveGel.IsUp = IsUp;
|
||||
ExplosiveGel.IsForward = IsForward;
|
||||
|
||||
normal = vectornormalize( trace["normal"] );
|
||||
angles = vectortoangles( normal );
|
||||
|
||||
angles += ( 90, 0, 0 );
|
||||
|
||||
ExplosiveGel.angles = angles;
|
||||
|
||||
ExplosiveGel SetModel( level.explosiveGelSettings.stuckMesh );
|
||||
ExplosiveGel.owner = self;
|
||||
ExplosiveGel SetOtherEnt(self);
|
||||
ExplosiveGel.killCamOffset = ( 0, 0, 55 );
|
||||
ExplosiveGel.killCamEnt = Spawn( "script_model", ExplosiveGel.origin + ExplosiveGel.killCamOffset );
|
||||
ExplosiveGel.stunned = false;
|
||||
|
||||
ExplosiveGel.weaponname = "explosive_gel_mp";
|
||||
|
||||
grenade delete();
|
||||
|
||||
level.mines[level.mines.size] = ExplosiveGel;
|
||||
|
||||
ExplosiveGel thread createBombSquadModel( level.explosiveGelSettings.gelBombSquadMesh, "tag_origin", self );
|
||||
ExplosiveGel thread mineBeacon();
|
||||
ExplosiveGel thread setExplosiveGelTeamHeadIcon( self.team );
|
||||
ExplosiveGel thread mineDamageMonitor();
|
||||
ExplosiveGel thread ExplosiveGelCountdownDetonation( self );
|
||||
|
||||
return ExplosiveGel;
|
||||
}
|
||||
|
||||
createBombSquadModel( modelName, tagName, owner )
|
||||
{
|
||||
apm_bombSquadModel = spawn( "script_model", (0,0,0) );
|
||||
apm_bombSquadModel hide();
|
||||
wait ( 0.05 );
|
||||
|
||||
apm_bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( owner );
|
||||
apm_bombSquadModel setModel( modelName );
|
||||
apm_bombSquadModel linkTo( self, tagName, (0,0,0), (0,0,0) );
|
||||
apm_bombSquadModel SetContents( 0 );
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
if ( isDefined(self.trigger) )
|
||||
self.trigger delete();
|
||||
|
||||
apm_bombSquadModel delete();
|
||||
}
|
||||
|
||||
mineBeacon() // self == apm_mine
|
||||
{
|
||||
effect["friendly"] = SpawnFx( level.explosiveGelSettings.beacon["friendly"], self getTagOrigin( "tag_fx" ) );
|
||||
effect["enemy"] = SpawnFx( level.explosiveGelSettings.beacon["enemy"], self getTagOrigin( "tag_fx" ) );
|
||||
|
||||
self thread mineBeaconTeamUpdater( effect );
|
||||
self waittill( "death" );
|
||||
|
||||
effect["friendly"] delete();
|
||||
effect["enemy"] delete();
|
||||
}
|
||||
|
||||
|
||||
mineBeaconTeamUpdater( effect, effect_flare ) // self == apm_mine
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
ownerTeam = self.owner.team;
|
||||
|
||||
// PlayFXOnTag fails if run on the same frame the parent entity was created
|
||||
wait ( 0.05 );
|
||||
|
||||
TriggerFx( effect["friendly"] );
|
||||
TriggerFx( effect["enemy"] );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
effect["friendly"] Hide();
|
||||
effect["enemy"] Hide();
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
if ( player.team == ownerTeam )
|
||||
{
|
||||
effect["friendly"] ShowToPlayer( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
effect["enemy"] ShowToPlayer( player );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( player == self.owner )
|
||||
{
|
||||
effect["friendly"] ShowToPlayer( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
effect["enemy"] ShowToPlayer( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
level waittill_either ( "joined_team", "player_spawned" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setExplosiveGelTeamHeadIcon( team )
|
||||
{
|
||||
self endon( "death" );
|
||||
wait .05;
|
||||
if ( level.teamBased )
|
||||
{
|
||||
if(self.IsUp==true || self.IsForward==true)
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( team, ( 0, 0, 28 ), undefined, true );
|
||||
else
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( team, ( 0, 0, 28 ) );
|
||||
}
|
||||
else if ( isDefined( self.owner ) )
|
||||
{
|
||||
if(self.IsUp==true)
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (28,0,28) );
|
||||
else
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (0,0,28) );
|
||||
}
|
||||
}
|
||||
|
||||
mineDamageMonitor() // self == apm_mine
|
||||
{
|
||||
self endon( "mine_triggered" );
|
||||
self endon( "mine_selfdestruct" );
|
||||
self endon( "death" );
|
||||
|
||||
self setcandamage( true );
|
||||
self.maxhealth = 100000;
|
||||
self.health = self.maxhealth;
|
||||
|
||||
attacker = undefined;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
if ( !isPlayer( attacker ) )
|
||||
continue;
|
||||
|
||||
// don't allow people to destroy mines on their team if FF is off
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
continue;
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
shortWeapon = maps\mp\_utility::strip_suffix( weapon, "_lefthand" );
|
||||
|
||||
switch( shortWeapon )
|
||||
{
|
||||
case "smoke_grenade_mp":
|
||||
case "smoke_grenade_var_mp":
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
self notify( "mine_destroyed" );
|
||||
|
||||
if ( isDefined( type ) && ( isSubStr( type, "MOD_GRENADE" ) || isSubStr( type, "MOD_EXPLOSIVE" ) ) )
|
||||
self.wasChained = true;
|
||||
|
||||
if ( isDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
self.wasDamaged = true;
|
||||
|
||||
if( isPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "bouncing_betty" );
|
||||
}
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
// "destroyed_explosive" notify, for challenges
|
||||
if ( isdefined( attacker ) && isdefined( attacker.pers[ "team" ] ) && isdefined( self.owner ) && isdefined( self.owner.pers[ "team" ] ) )
|
||||
{
|
||||
if ( attacker.pers[ "team" ] != self.owner.pers[ "team" ] )
|
||||
attacker notify( "destroyed_explosive" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// checking isDefined attacker is defensive but it's too late in the project to risk issues by not having it
|
||||
if ( isDefined( self.owner ) && isDefined( attacker ) && attacker != self.owner )
|
||||
attacker notify( "destroyed_explosive" );
|
||||
}
|
||||
|
||||
self thread mineExplode( attacker );
|
||||
}
|
||||
|
||||
|
||||
mineExplode( attacker )
|
||||
{
|
||||
if ( !IsDefined( self ) || !IsDefined(self.owner) )
|
||||
return;
|
||||
|
||||
// using a passed in attacker means that the owner wasn't the one who detonated this, this way the correct credit will go to the correct player
|
||||
if( !IsDefined( attacker ) )
|
||||
attacker = self.owner;
|
||||
|
||||
self PlaySound( "null" );
|
||||
|
||||
tagOrigin = self GetTagOrigin( "tag_fx" );
|
||||
PlayFX( level.explosiveGelSettings.gelExplosionFx, tagOrigin );
|
||||
|
||||
wait( 0.05 ); // needed or the effect doesn't play
|
||||
if ( !IsDefined( self ) || !IsDefined(self.owner) )
|
||||
return;
|
||||
|
||||
self Hide();
|
||||
|
||||
//cause damage
|
||||
self RadiusDamage( self.origin, CONST_Mine_DamageRadius, CONST_Mine_DamageMax, CONST_Mine_DamageMin, attacker, "MOD_EXPLOSIVE");
|
||||
|
||||
if ( IsDefined( self.owner ) && IsDefined( level.leaderDialogOnPlayer_func ) )
|
||||
self.owner thread [[ level.leaderDialogOnPlayer_func ]]( "mine_destroyed", undefined, undefined, self.origin );
|
||||
|
||||
wait( 0.2 );
|
||||
if ( !IsDefined( self ) || !IsDefined(self.owner) )
|
||||
return;
|
||||
|
||||
self thread apm_mine_deleteKillCamEnt();
|
||||
|
||||
self notify ( "death" );
|
||||
|
||||
|
||||
if(IsDefined(self.pickuptrigger))
|
||||
self.pickuptrigger delete();
|
||||
|
||||
self hide();
|
||||
}
|
||||
|
||||
EarlyDetonate( grenade )
|
||||
{
|
||||
self notify( "earlyNotify" );
|
||||
|
||||
tagOrigin = grenade GetTagOrigin( "tag_fx" );
|
||||
PlayFX( level.explosiveGelSettings.gelExplosionFx, tagOrigin );
|
||||
|
||||
grenade Detonate();
|
||||
}
|
||||
|
||||
apm_mine_deleteKillCamEnt()
|
||||
{
|
||||
wait ( 3 );
|
||||
self.killCamEnt delete();
|
||||
self delete();
|
||||
|
||||
level.mines = array_removeUndefined(level.mines);
|
||||
|
||||
}
|
||||
|
||||
ExplosiveGelCountdownDetonation( Owner )
|
||||
{
|
||||
self endon( "mine_destroyed" );
|
||||
self endon( "mine_selfdestruct" );
|
||||
self endon( "death" );
|
||||
|
||||
wait 3;
|
||||
|
||||
self notify( "mine_triggered" );
|
||||
|
||||
self thread mineExplode();
|
||||
}
|
296
raw/maps/mp/_extrahealth.gsc
Normal file
296
raw/maps/mp/_extrahealth.gsc
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
Exo Stim
|
||||
Author: Keith Evans
|
||||
Description: A piece of equipment used to give you temporarily increased speed
|
||||
*/
|
||||
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\perks\_perkfunctions;
|
||||
#include maps\mp\_snd_common_mp;
|
||||
|
||||
CONST_EXTRA_HEALTH_MULTIPLIER = 1.4;
|
||||
|
||||
watchExtraHealthUsage() // self = player
|
||||
{
|
||||
self notify( "exo_health_taken" ); // Cleans up existing Exo Stim threads.
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_health_taken" );
|
||||
|
||||
if ( !self HasWeapon( "extra_health_mp" ) )
|
||||
return;
|
||||
|
||||
ExtraHealthInit();
|
||||
|
||||
self thread MonitorPlayerDeath();
|
||||
self thread wait_for_game_end();
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "exo_ability_activate", weapname );
|
||||
if ( weapname == "extra_health_mp" )
|
||||
{
|
||||
if( !IsAlive( self ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
thread tryUseExtraHealth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExtraHealthInit() // self = player
|
||||
{
|
||||
self.exo_health_on = false;
|
||||
|
||||
self BatterySetDischargeScale( "extra_health_mp", 1.0 );
|
||||
full_energy = self BatteryGetSize( "extra_health_mp" );
|
||||
|
||||
if ( self GetTacticalWeapon() == "extra_health_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req0", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total0", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level0", full_energy );
|
||||
}
|
||||
else if ( self GetLethalWeapon() == "extra_health_mp" )
|
||||
{
|
||||
self SetClientOmnvar( "exo_ability_nrg_req1", 0 );
|
||||
self SetClientOmnvar( "exo_ability_nrg_total1", full_energy );
|
||||
self SetClientOmnvar( "ui_exo_battery_level1", full_energy );
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.exo_health_le_inactive_vfx ) )
|
||||
level.exo_health_le_inactive_vfx = LoadFX( "vfx/unique/exo_health_le_inactive" );
|
||||
if ( !IsDefined( level.exo_health_le_active_vfx ) )
|
||||
level.exo_health_le_active_vfx = LoadFX( "vfx/unique/exo_health_le_active" );
|
||||
if ( !IsDefined( level.exo_health_rt_inactive_vfx ) )
|
||||
level.exo_health_rt_inactive_vfx = LoadFX( "vfx/unique/exo_health_rt_inactive" );
|
||||
if ( !IsDefined( level.exo_health_rt_active_vfx ) )
|
||||
level.exo_health_rt_active_vfx = LoadFX( "vfx/unique/exo_health_rt_active" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !inVirtualLobby() )
|
||||
{
|
||||
// Turn on fx for inactive exo health
|
||||
PlayFXOnTag( level.exo_health_le_inactive_vfx, self, "J_Shoulder_LE" );
|
||||
PlayFXOnTag( level.exo_health_rt_inactive_vfx, self, "J_Shoulder_RI" );
|
||||
}
|
||||
}
|
||||
|
||||
tryUseExtraHealth( ) // self = player
|
||||
{
|
||||
self endon( "exo_health_taken" );
|
||||
|
||||
//thread PrintHealthToScreen();
|
||||
|
||||
if ( self.exo_health_on == true )
|
||||
{
|
||||
self thread StopExtraHealth( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread StartExtraHealth();
|
||||
}
|
||||
}
|
||||
|
||||
killStimFx() // self == player
|
||||
{
|
||||
if ( IsDefined( self.stim_fx_l ) )
|
||||
{
|
||||
self.stim_fx_l delete();
|
||||
self.stim_fx_l = undefined;
|
||||
}
|
||||
if ( IsDefined( self.stim_fx_r ) )
|
||||
{
|
||||
self.stim_fx_r delete();
|
||||
self.stim_fx_r = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
StartExtraHealth() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_health_taken" );
|
||||
self endon( "EndExtraHealth" );
|
||||
|
||||
// Extra Health
|
||||
self.exo_health_on = true;
|
||||
|
||||
self.maxhealth = int( self.maxhealth * CONST_EXTRA_HEALTH_MULTIPLIER );
|
||||
self.ignoreRegenDelay = true;
|
||||
self.healthRegenLevel = .99;
|
||||
self notify( "damage" );
|
||||
|
||||
self BatteryDischargeBegin( "extra_health_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "extra_health_mp", "ui_exo_battery_toggle", 1 );
|
||||
|
||||
self thread maps\mp\_exo_battery::update_exo_battery_hud( "extra_health_mp" );
|
||||
|
||||
self thread monitory_health_battery_charge();
|
||||
|
||||
// activation sound
|
||||
self snd_message( "mp_exo_health_activate" );
|
||||
|
||||
//swap to active vfx
|
||||
self killStimFx();
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !self.exo_health_on ) //turned off during wait?
|
||||
return;
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.stim_fx_l = SpawnLinkedFX( level.exo_health_le_active_vfx, self, "J_Shoulder_LE" );
|
||||
self.stim_fx_r = SpawnLinkedFX( level.exo_health_rt_active_vfx, self, "J_Shoulder_RI" );
|
||||
TriggerFX( self.stim_fx_l );
|
||||
TriggerFX( self.stim_fx_r );
|
||||
}
|
||||
|
||||
//jwells - overlay VFX handled in code so killcam will work
|
||||
}
|
||||
|
||||
StopExtraHealth( should_play_fx ) // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_health_taken" );
|
||||
|
||||
if ( !isDefined( self.exo_health_on ) || !self.exo_health_on )
|
||||
return;
|
||||
|
||||
self notify( "EndExtraHealth" );
|
||||
|
||||
// If MonitorPlayerDeath() gets called while this function is running, this function will end and MonitorPlayerDeath() will clean up.
|
||||
self endon( "EndExtraHealth" );
|
||||
|
||||
if ( !IsDefined( should_play_fx ) )
|
||||
{
|
||||
should_play_fx = true;
|
||||
}
|
||||
|
||||
self.exo_health_on = false;
|
||||
|
||||
if ( isdefined ( level.isHorde ) )
|
||||
{
|
||||
class = self GetClientOmnvar ( "ui_horde_player_class" );
|
||||
self.maxhealth = self.classSettings[class]["classhealth"];
|
||||
}
|
||||
else
|
||||
self.maxhealth = int( self.maxhealth / CONST_EXTRA_HEALTH_MULTIPLIER );
|
||||
|
||||
if( self.health > self.maxhealth )
|
||||
self.health = self.maxhealth;
|
||||
|
||||
self.healthRegenLevel = undefined;
|
||||
|
||||
self BatteryDischargeEnd( "extra_health_mp" );
|
||||
|
||||
self maps\mp\_exo_battery::set_exo_ability_hud_omnvar( "extra_health_mp", "ui_exo_battery_toggle", 0 );
|
||||
|
||||
self killStimFx();
|
||||
|
||||
if ( should_play_fx == true )
|
||||
{
|
||||
// deactivation sound
|
||||
self snd_message( "mp_exo_health_deactivate" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
if ( !IsDefined( self.exo_cloak_on ) || self.exo_cloak_on == false )
|
||||
{
|
||||
self.stim_fx_l = SpawnLinkedFx( level.exo_health_le_inactive_vfx, self, "J_Shoulder_LE" );
|
||||
self.stim_fx_r = SpawnLinkedFx( level.exo_health_rt_inactive_vfx, self, "J_Shoulder_RI" );
|
||||
TriggerFX( self.stim_fx_l );
|
||||
TriggerFX( self.stim_fx_r );
|
||||
}
|
||||
}
|
||||
|
||||
//jwells - overlay VFX handled in code so killcam will work
|
||||
}
|
||||
|
||||
MonitorPlayerDeath( ) // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self waittill_any( "death", "joined_team", "faux_spawn", "exo_health_taken" );
|
||||
|
||||
self thread StopExtraHealth( false );
|
||||
}
|
||||
|
||||
monitory_health_battery_charge() // self = player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_health_taken" );
|
||||
|
||||
while ( self.exo_health_on == true )
|
||||
{
|
||||
if ( self BatteryGetCharge( "extra_health_mp" ) <= 0 )
|
||||
{
|
||||
self thread StopExtraHealth( true );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
take_exo_health() //self = player
|
||||
{
|
||||
self notify( "kill_battery" );
|
||||
self notify( "exo_health_taken" );
|
||||
self takeWeapon( "extra_health_mp" );
|
||||
}
|
||||
|
||||
give_exo_health() //self = player
|
||||
{
|
||||
self giveWeapon( "extra_health_mp" );
|
||||
self thread watchExtraHealthUsage();
|
||||
}
|
||||
|
||||
wait_for_game_end() // self = player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "exo_health_taken" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
self thread StopExtraHealth( false );
|
||||
}
|
||||
|
||||
/#
|
||||
PrintHealthToScreen() // self = player
|
||||
{
|
||||
self endon("EndExtraHealth");
|
||||
self endon("death");
|
||||
self endon( "exo_health_taken" );
|
||||
|
||||
while(true)
|
||||
{
|
||||
IPrintLnBold(self.health);
|
||||
wait 1;
|
||||
}
|
||||
}
|
||||
#/
|
138
raw/maps/mp/_fastheal.gsc
Normal file
138
raw/maps/mp/_fastheal.gsc
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
Exo Regen
|
||||
Author: Keith Evans
|
||||
Description: A piece of equipment used to give you temporarily increased speed
|
||||
*/
|
||||
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\perks\_perkfunctions;
|
||||
|
||||
CONST_EXO_REGEN_DURATION = 10;
|
||||
|
||||
watchFastHealUsage()
|
||||
{
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
if(!IsDefined(level.fastHealSettings))
|
||||
{
|
||||
FastHealInit();
|
||||
}
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "grenade_fire", grenade, weapname );
|
||||
if ( weapname == "fast_heal_mp" )
|
||||
{
|
||||
if( !IsAlive( self ) )
|
||||
{
|
||||
grenade delete();
|
||||
return;
|
||||
}
|
||||
|
||||
thread tryUseFastHeal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FastHealInit()
|
||||
{
|
||||
self.fastHealSettings = SpawnStruct();
|
||||
}
|
||||
|
||||
tryUseFastHeal( ) // self == player
|
||||
{
|
||||
// Force init (this is for test bots)
|
||||
if(!IsDefined(self.fastHealSettings))
|
||||
{
|
||||
FastHealInit();
|
||||
}
|
||||
|
||||
//thread PrintHealthToScreen();
|
||||
|
||||
ResetFastHeal();
|
||||
thread StartFastHeal();
|
||||
thread MonitorPlayerDeath();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StartFastHeal()
|
||||
{
|
||||
self endon("ClearFastHeal");
|
||||
self endon("death");
|
||||
|
||||
//temp activation sound
|
||||
self playLocalSound( "earn_superbonus" );
|
||||
|
||||
//temp hud overlay
|
||||
self.fastHealSettings.overlay = NewClientHudElem( self );
|
||||
self.fastHealSettings.overlay.x = 0;
|
||||
self.fastHealSettings.overlay.y = 0;
|
||||
self.fastHealSettings.overlay.horzAlign = "fullscreen";
|
||||
self.fastHealSettings.overlay.vertAlign = "fullscreen";
|
||||
self.fastHealSettings.overlay SetShader( "exo_hud_cloak_overlay", 640, 480 );
|
||||
self.fastHealSettings.overlay.archive = true;
|
||||
self.fastHealSettings.overlay.alpha = 1.0;
|
||||
|
||||
self.IsFastHeal = true;
|
||||
self.ignoreRegenDelay = true;
|
||||
|
||||
//Faster Heal
|
||||
self.healthRegenLevel = .99;
|
||||
self notify("damage");
|
||||
|
||||
wait CONST_EXO_REGEN_DURATION;
|
||||
|
||||
self.healthRegenLevel = undefined;
|
||||
|
||||
self.IsFastHeal = false;
|
||||
|
||||
if( IsDefined( self.fastHealSettings.overlay ) )
|
||||
self.fastHealSettings.overlay Destroy();
|
||||
|
||||
self notify("EndFastHeal");
|
||||
}
|
||||
|
||||
ResetFastHeal()
|
||||
{
|
||||
if(IsDefined(self.IsFastHeal) && self.IsFastHeal == true)
|
||||
{
|
||||
if( IsDefined( self.fastHealSettings.overlay ) )
|
||||
self.fastHealSettings.overlay Destroy();
|
||||
|
||||
self.healthRegenLevel = undefined;
|
||||
self notify("ClearFastHeal");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MonitorPlayerDeath( )
|
||||
{
|
||||
self endon( "EndFastHeal" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self.healthRegenLevel = undefined;
|
||||
self.IsFastHeal = false;
|
||||
|
||||
if( IsDefined( self.fastHealSettings.overlay ) )
|
||||
self.fastHealSettings.overlay Destroy();
|
||||
}
|
||||
|
||||
PrintHealthToScreen()
|
||||
{
|
||||
self endon("EndFastHeal");
|
||||
self endon("death");
|
||||
|
||||
while(true)
|
||||
{
|
||||
IPrintLnBold(self.health);
|
||||
wait 1;
|
||||
}
|
||||
}
|
175
raw/maps/mp/_flashgrenades.gsc
Normal file
175
raw/maps/mp/_flashgrenades.gsc
Normal file
@ -0,0 +1,175 @@
|
||||
#include maps\mp\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
PreCacheShellShock("flashbang_mp");
|
||||
}
|
||||
|
||||
|
||||
startMonitoringFlash()
|
||||
{
|
||||
self thread monitorFlash();
|
||||
}
|
||||
|
||||
|
||||
stopMonitoringFlash(disconnected)
|
||||
{
|
||||
self notify("stop_monitoring_flash");
|
||||
}
|
||||
|
||||
|
||||
flashRumbleLoop( duration )
|
||||
{
|
||||
self endon("stop_monitoring_flash");
|
||||
|
||||
self endon("flash_rumble_loop");
|
||||
self notify("flash_rumble_loop");
|
||||
|
||||
goalTime = getTime() + duration * 1000;
|
||||
|
||||
while ( getTime() < goalTime )
|
||||
{
|
||||
self PlayRumbleOnEntity( "damage_heavy" );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
monitorFlash()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
self notify("monitorFlash");
|
||||
self endon("monitorFlash");
|
||||
|
||||
self.flashEndTime = 0;
|
||||
|
||||
durationMultiplier = 2.5; // how long the flash lasts
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill( "flashbang", origin, percent_distance, percent_angle, attacker, teamName, extraDuration );
|
||||
|
||||
if ( !IsAlive( self ) )
|
||||
break;
|
||||
|
||||
if ( IsDefined( self.usingRemote ) )
|
||||
continue;
|
||||
|
||||
if( !IsDefined( extraDuration ) )
|
||||
extraDuration = 0;
|
||||
|
||||
hurtattacker = false;
|
||||
hurtvictim = true;
|
||||
|
||||
// with this being the 9-bang we shouldn't consider angle as a variable because of the multiple blasts
|
||||
if ( percent_angle < 0.25 )
|
||||
percent_angle = 0.25;
|
||||
else if ( percent_angle > 0.8 )
|
||||
percent_angle = 1;
|
||||
|
||||
duration = percent_distance * percent_angle * durationMultiplier;
|
||||
duration += extraDuration;
|
||||
|
||||
// MW3 stun resistance perk
|
||||
if ( IsDefined( self.stunScaler ) )
|
||||
duration = duration * self.stunScaler;
|
||||
|
||||
if ( duration < 0.25 )
|
||||
continue;
|
||||
|
||||
rumbleduration = undefined;
|
||||
if ( duration > 2 )
|
||||
rumbleduration = 0.75;
|
||||
else
|
||||
rumbleduration = 0.25;
|
||||
|
||||
assert(IsDefined(self.team));
|
||||
if (level.teamBased && IsDefined(attacker) && IsDefined(attacker.team) && attacker.team == self.team && attacker != self)
|
||||
{
|
||||
if(level.friendlyfire == 0) // no FF
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(level.friendlyfire == 1) // FF
|
||||
{
|
||||
}
|
||||
else if(level.friendlyfire == 2) // reflect
|
||||
{
|
||||
duration = duration * .5;
|
||||
rumbleduration = rumbleduration * .5;
|
||||
hurtvictim = false;
|
||||
hurtattacker = true;
|
||||
}
|
||||
else if(level.friendlyfire == 3) // share
|
||||
{
|
||||
duration = duration * .5;
|
||||
rumbleduration = rumbleduration * .5;
|
||||
hurtattacker = true;
|
||||
}
|
||||
}
|
||||
else if( IsDefined(attacker) )
|
||||
{
|
||||
if( attacker != self )
|
||||
attacker maps\mp\gametypes\_missions::processChallenge( "ch_indecentexposure" );
|
||||
}
|
||||
|
||||
if ( hurtvictim && IsDefined(self) )
|
||||
{
|
||||
self thread applyFlash(duration, rumbleduration);
|
||||
|
||||
if ( IsDefined(attacker) && attacker != self )
|
||||
{
|
||||
attacker thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "flash" );
|
||||
|
||||
// need this here because there are instances where you can not take damage from a flashbang but still get flashed
|
||||
// since you don't take damage the recon perk won't paint you when it should
|
||||
// show the victim on the minimap for N seconds
|
||||
victim = self;
|
||||
if( IsPlayer( attacker ) && attacker IsItemUnlocked( "specialty_paint" ) && attacker _hasPerk( "specialty_paint" ) )
|
||||
{
|
||||
if( !victim maps\mp\perks\_perkfunctions::isPainted() )
|
||||
attacker maps\mp\gametypes\_missions::processChallenge( "ch_paint_pro" );
|
||||
|
||||
victim thread maps\mp\perks\_perkfunctions::setPainted( attacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( hurtattacker && IsDefined(attacker) )
|
||||
{
|
||||
attacker thread applyFlash(duration, rumbleduration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyFlash(duration, rumbleduration)
|
||||
{
|
||||
// wait for the highest flash duration this frame,
|
||||
// and apply it in the following frame
|
||||
|
||||
if (!IsDefined(self.flashDuration) || duration > self.flashDuration)
|
||||
self.flashDuration = duration;
|
||||
if (!IsDefined(self.flashRumbleDuration) || rumbleduration > self.flashRumbleDuration)
|
||||
self.flashRumbleDuration = rumbleduration;
|
||||
|
||||
wait .05;
|
||||
|
||||
if (IsDefined(self.flashDuration))
|
||||
{
|
||||
self shellshock( "flashbang_mp", self.flashDuration ); // TODO: avoid shellshock overlap
|
||||
self.flashEndTime = getTime() + (self.flashDuration * 1000);
|
||||
self thread light_set_override_for_player( "flashed", 0.1, 0.1, ( self.flashDuration - 0.1 ) );
|
||||
}
|
||||
if (IsDefined(self.flashRumbleDuration)) {
|
||||
self thread flashRumbleLoop( self.flashRumbleDuration ); //TODO: Non-hacky rumble.
|
||||
}
|
||||
|
||||
self.flashDuration = undefined;
|
||||
self.flashRumbleDuration = undefined;
|
||||
}
|
||||
|
||||
|
||||
isFlashbanged()
|
||||
{
|
||||
return IsDefined( self.flashEndTime ) && gettime() < self.flashEndTime;
|
||||
}
|
219
raw/maps/mp/_fx.gsc
Normal file
219
raw/maps/mp/_fx.gsc
Normal file
@ -0,0 +1,219 @@
|
||||
#include common_scripts\utility;
|
||||
#include common_scripts\_fx;
|
||||
#include common_scripts\_createfx;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\_createfx;
|
||||
|
||||
/*
|
||||
****************************************************************************************************************
|
||||
OneShotfx: Fires an effect once.
|
||||
maps\mp\_fx::OneShotfx( effectname, (x y z), predelay);
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::OneShotfx(level.medFire, // Medium fire effect
|
||||
(-701, -18361, 148), // Origin
|
||||
5); // Wait 5 seconds before doing effect
|
||||
****************************************************************************************************************
|
||||
|
||||
|
||||
****************************************************************************************************************
|
||||
Loopfx: Loops an effect with a waittime.
|
||||
maps\mp\_fx::loopfx( effectname, (x y z), delay_between_shots);
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::loopfx(level.medFire, // Medium fire effect
|
||||
(-701, -18361, 148), // Origin
|
||||
0.3); // Wait 0.3 seconds between shots
|
||||
****************************************************************************************************************
|
||||
|
||||
|
||||
****************************************************************************************************************
|
||||
GunFireLoopfx: Simulates bursts of fire.
|
||||
maps\mp\_fx::gunfireloopfx(fxId, fxPos, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax)
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::gunfireloopfx (level.medFire, // Medium fire effect
|
||||
(-701, -18361, 148), // Origin
|
||||
10, 15, // 10 to 15 shots
|
||||
0.1, 0.3, // 0.1 to 0.3 seconds between shots
|
||||
2.5, 9); // 2.5 to 9 seconds between sets of shots.
|
||||
****************************************************************************************************************
|
||||
|
||||
****************************************************************************************************************
|
||||
GrenadeExplosionfx: Creates a grenade explosion with view jitter.
|
||||
maps\mp\_fx::GrenadeExplosionfx((x y z));
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::GrenadeExplosionfx( (-701, -18361, 148) ); // origin
|
||||
****************************************************************************************************************
|
||||
*/
|
||||
|
||||
script_print_fx()
|
||||
{
|
||||
if ( ( !isdefined( self.script_fxid ) ) || ( !isdefined( self.script_fxcommand ) ) || ( !isdefined( self.script_delay ) ) )
|
||||
{
|
||||
println( "Effect at origin ", self.origin, " doesn't have script_fxid/script_fxcommand/script_delay" );
|
||||
self delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isdefined( self.target ) )
|
||||
org = getent( self.target ).origin;
|
||||
else
|
||||
org = "undefined";
|
||||
|
||||
// println ("^a Command:", self.script_fxcommand, " Effect:", self.script_fxID, " Delay:", self.script_delay, " ", self.origin);
|
||||
if ( self.script_fxcommand == "OneShotfx" )
|
||||
println( "maps\mp\_fx::OneShotfx(\"" + self.script_fxid + "\", " + self.origin + ", " + self.script_delay + ", " + org + ");" );
|
||||
|
||||
if ( self.script_fxcommand == "loopfx" )
|
||||
println( "maps\mp\_fx::LoopFx(\"" + self.script_fxid + "\", " + self.origin + ", " + self.script_delay + ", " + org + ");" );
|
||||
|
||||
if ( self.script_fxcommand == "loopsound" )
|
||||
println( "maps\mp\_fx::LoopSound(\"" + self.script_fxid + "\", " + self.origin + ", " + self.script_delay + ", " + org + ");" );
|
||||
}
|
||||
|
||||
|
||||
GrenadeExplosionfx( pos )
|
||||
{
|
||||
playfx( level._effect[ "mechanical explosion" ], pos );
|
||||
earthquake( 0.15, 0.5, pos, 250 );
|
||||
// TODO: Add explosion effect and view jitter
|
||||
// println("The script command grenadeExplosionEffect has been removed. maps\\mp\\_fx::GrenadeExplosionfx must be set up to make an effect and jitter the view.");
|
||||
}
|
||||
|
||||
|
||||
soundfx( fxId, fxPos, endonNotify )
|
||||
{
|
||||
org = spawn( "script_origin", ( 0, 0, 0 ) );
|
||||
org.origin = fxPos;
|
||||
org playloopsound( fxId );
|
||||
if ( isdefined( endonNotify ) )
|
||||
org thread soundfxDelete( endonNotify );
|
||||
|
||||
/*
|
||||
ent = level thread createfx_showOrigin ( fxId, fxPos, undefined, undefined, "soundfx" );
|
||||
ent.delay = 0;
|
||||
ent endon ("effect deleted");
|
||||
ent.soundfx = org;
|
||||
*/
|
||||
}
|
||||
|
||||
soundfxDelete( endonNotify )
|
||||
{
|
||||
level waittill( endonNotify );
|
||||
self delete();
|
||||
}
|
||||
|
||||
|
||||
/*rainfx( fxId, fxId2, fxPos )
|
||||
{
|
||||
org = spawn( "script_origin", ( 0, 0, 0 ) );
|
||||
org.origin = fxPos;
|
||||
org thread rainLoop( fxId, fxId2 );
|
||||
|
||||
//ent = level thread createfx_showOrigin( fxId, fxPos, undefined, undefined, "rainfx", undefined, fxId2 );
|
||||
//ent.delay = 0;
|
||||
//ent endon ("effect deleted");
|
||||
//ent.soundfx = org;
|
||||
}
|
||||
|
||||
rainLoop( hardRain, lightRain )
|
||||
{
|
||||
// org playloopsound (fxId);
|
||||
self endon( "death" );
|
||||
blend = spawn( "sound_blend", ( 0.0, 0.0, 0.0 ) );
|
||||
blend.origin = self.origin;
|
||||
self thread blendDelete( blend );
|
||||
|
||||
blend2 = spawn( "sound_blend", ( 0.0, 0.0, 0.0 ) );
|
||||
blend2.origin = self.origin;
|
||||
self thread blendDelete( blend2 );
|
||||
|
||||
|
||||
// lerp of 0 will play _null only
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, 0 );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, 1 );
|
||||
rain = "hard";
|
||||
blendTime = undefined;
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill( "rain_change", change, blendTime );
|
||||
blendTime *= 20;// internal framerate
|
||||
assert( change == "hard" || change == "light" || change == "none" );
|
||||
assert( blendtime > 0 );
|
||||
|
||||
if ( change == "hard" )
|
||||
{
|
||||
if ( rain == "none" )
|
||||
{
|
||||
blendTime *= 0.5;// gotta do 2 blends to go from none to hard
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, i / blendtime );
|
||||
wait( 0.05 );
|
||||
}
|
||||
rain = "light";
|
||||
}
|
||||
if ( rain == "light" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, 1 - ( i / blendtime ) );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, i / blendtime );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( change == "none" )
|
||||
{
|
||||
if ( rain == "hard" )
|
||||
{
|
||||
blendTime *= 0.5;// gotta do 2 blends to go from hard to none
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, ( i / blendtime ) );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, 1 - ( i / blendtime ) );
|
||||
wait( 0.05 );
|
||||
}
|
||||
rain = "light";
|
||||
}
|
||||
if ( rain == "light" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, 1 - ( i / blendtime ) );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( change == "light" )
|
||||
{
|
||||
if ( rain == "none" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, i / blendtime );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
if ( rain == "hard" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, i / blendtime );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, 1 - ( i / blendtime ) );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rain = change;
|
||||
}
|
||||
}*/
|
||||
|
||||
blendDelete( blend )
|
||||
{
|
||||
self waittill( "death" );
|
||||
blend delete();
|
||||
}
|
43
raw/maps/mp/_global_fx.gsc
Normal file
43
raw/maps/mp/_global_fx.gsc
Normal file
@ -0,0 +1,43 @@
|
||||
#include maps\mp\_global_fx_code;
|
||||
|
||||
// This script automaticly plays a users specified oneshot effect on all prefabs that have the
|
||||
// specified "script_struct" and "targetname" It also excepts angles from the "script_struct"
|
||||
// but will set a default angle of ( 0, 0, 0 ) if none is defined.
|
||||
|
||||
main()
|
||||
{
|
||||
// targetname fxFile
|
||||
global_FX( "ch_streetlight_02_FX_origin" , "fx/misc/lighthaze" );
|
||||
global_FX( "me_streetlight_01_FX_origin" , "fx/misc/lighthaze_bog_a" );
|
||||
global_FX( "ch_street_light_01_on" , "fx/misc/light_glow_white" );
|
||||
global_FX( "lamp_post_globe_on" , "fx/misc/light_glow_white" );
|
||||
global_FX( "highway_lamp_post" , "fx/misc/lighthaze_villassault" );
|
||||
global_FX( "cs_cargoship_spotlight_on_FX_origin" , "fx/misc/lighthaze" );
|
||||
global_FX( "com_tires_burning01_FX_origin" , "fx/fire/tire_fire_med" );
|
||||
global_FX( "icbm_powerlinetower_FX_origin" , "fx/misc/power_tower_light_red_blink" );
|
||||
global_FX( "icbm_mainframe_FX_origin" , "fx/props/icbm_mainframe_lightblink" );
|
||||
global_FX( "lighthaze_oilrig_FX_origin" , "fx/misc/lighthaze_oilrig" );
|
||||
global_FX( "lighthaze_white_FX_origin" , "fx/misc/lighthaze_white" );
|
||||
global_FX( "light_glow_walllight_white_FX_origin", "fx/misc/light_glow_walllight_white" );
|
||||
global_FX( "fluorescent_glow_FX_origin" , "fx/misc/fluorescent_glow" );
|
||||
global_FX( "light_glow_industrial_FX_origin" , "fx/misc/light_glow_industrial" );
|
||||
global_FX( "highrise_blinky_tower" , "fx/misc/power_tower_light_red_blink_large" );
|
||||
global_FX( "light_glow_white_bulb_FX_origin" , "fx/misc/light_glow_white_bulb" );
|
||||
global_FX( "light_glow_white_lamp_FX_origin" , "fx/misc/light_glow_white_lamp" );
|
||||
|
||||
// targetname fxFile delay
|
||||
global_FX( "light_red_steady_FX_origin" , "fx/misc/tower_light_red_steady" , -2 );
|
||||
global_FX( "light_blue_steady_FX_origin" , "vfx/lights/tower_light_glow_blue_steady", -2 );
|
||||
global_FX( "light_orange_steady_FX_origin" , "fx/misc/tower_light_orange_steady" , -2 );
|
||||
global_FX( "glow_stick_pile_FX_origin" , "fx/misc/glow_stick_glow_pile" , -2 );
|
||||
global_FX( "glow_stick_orange_pile_FX_origin", "fx/misc/glow_stick_glow_pile_orange" , -2 );
|
||||
global_FX( "light_pulse_red_FX_origin" , "fx/misc/light_glow_red_generic_pulse" , -2 );
|
||||
global_FX( "light_pulse_red_FX_origin" , "fx/misc/light_glow_red_generic_pulse" , -2 );
|
||||
global_FX( "light_pulse_orange_FX_origin" , "fx/misc/light_glow_orange_generic_pulse", -2 );
|
||||
global_FX( "light_red_blink_FX_origin" , "fx/misc/power_tower_light_red_blink" , -2 );
|
||||
|
||||
// targetname fxFile delay fxName soundalias
|
||||
global_FX( "flare_ambient_FX_origin" , "fx/misc/flare_ambient" , undefined, undefined, "emt_road_flare_burn" );
|
||||
global_FX( "me_dumpster_fire_FX_origin", "fx/fire/firelp_med_pm" , undefined, undefined, "fire_dumpster_medium" );
|
||||
global_FX( "barrel_fireFX_origin" , "fx/fire/firelp_barrel_pm", undefined, undefined, "fire_barrel_small" );
|
||||
}
|
35
raw/maps/mp/_global_fx_code.gsc
Normal file
35
raw/maps/mp/_global_fx_code.gsc
Normal file
@ -0,0 +1,35 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
global_FX( targetname, fxFile, delay, fxName, soundalias )
|
||||
{
|
||||
// script_structs
|
||||
ents = getstructarray( targetname, "targetname" );
|
||||
if ( ents.size <= 0 )
|
||||
return;
|
||||
|
||||
if ( ! IsDefined( delay ) )
|
||||
delay = RandomFloatRange( -20, -15 );
|
||||
|
||||
if ( !IsDefined( fxName ) )
|
||||
fxName = fxFile;
|
||||
|
||||
foreach ( fxEnt in ents )
|
||||
{
|
||||
if ( !IsDefined( level._effect ) )
|
||||
level._effect = [];
|
||||
if ( !IsDefined( level._effect[ fxName ] ) )
|
||||
level._effect[ fxName ] = LoadFX( fxFile );
|
||||
|
||||
// default effect angles if they dont exist
|
||||
if ( !IsDefined( fxEnt.angles ) )
|
||||
fxEnt.angles = ( 0, 0, 0 );
|
||||
|
||||
ent = createOneshotEffect( fxName );
|
||||
ent.v[ "origin" ] = ( fxEnt.origin );
|
||||
ent.v[ "angles" ] = ( fxEnt.angles );
|
||||
ent.v[ "fxid" ] = fxName;
|
||||
ent.v[ "delay" ] = delay;
|
||||
if ( IsDefined( soundalias ) )
|
||||
ent.v[ "soundalias" ] = soundalias;
|
||||
}
|
||||
}
|
85
raw/maps/mp/_grappling_hook.gsc
Normal file
85
raw/maps/mp/_grappling_hook.gsc
Normal file
@ -0,0 +1,85 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
if ( isGrapplingHookGameMode() )
|
||||
{
|
||||
thread grappling_hook_on_player_spawn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
grappling_hook_on_player_spawn()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "player_spawned", player );
|
||||
player give_grappling_hook();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
give_grappling_hook() // self = player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
grapplingHookWeapon = get_grappling_hook_weapon();
|
||||
|
||||
if ( self HasWeapon( grapplingHookWeapon ) )
|
||||
return;
|
||||
|
||||
self SetTacticalWeapon( grapplingHookWeapon );
|
||||
self giveWeapon( grapplingHookWeapon );
|
||||
|
||||
self notify( "grappling_hook_reset" ); // clear any old threads that were spawned from a previous iteration of this function... this is required for timing reasons
|
||||
|
||||
self thread wait_for_grappling_hook_pressed();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
wait_for_grappling_hook_pressed() // should be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "grappling_hook_reset" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "exo_ability_activate", weaponName );
|
||||
|
||||
if ( weaponName == level.grapplingHookWeapon )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
take_grappling_hook() // self = player, should not be threaded
|
||||
//==============================================================================
|
||||
{
|
||||
grapplingHookWeapon = get_grappling_hook_weapon();
|
||||
|
||||
self takeWeapon( grapplingHookWeapon );
|
||||
self notify( "grappling_hook_reset" );
|
||||
}
|
||||
|
||||
|
||||
get_grappling_hook_weapon()
|
||||
{
|
||||
if ( IsDefined( level.grapplingHookWeapon ) )
|
||||
return level.grapplingHookWeapon;
|
||||
|
||||
level.grapplingHookWeapon = "iw5_dlcgun12loot7_mp";
|
||||
|
||||
return level.grapplingHookWeapon;
|
||||
}
|
116
raw/maps/mp/_lasersight.gsc
Normal file
116
raw/maps/mp/_lasersight.gsc
Normal file
@ -0,0 +1,116 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
lasersight_think()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
self.laser_on = undefined;
|
||||
self.wasEMP = false;
|
||||
|
||||
while( true )
|
||||
{
|
||||
|
||||
has_laser = false;
|
||||
|
||||
attachment_list = GetWeaponAttachments( self GetCurrentWeapon() );
|
||||
|
||||
if( isDefined( attachment_list ) )
|
||||
{
|
||||
foreach( attachment in attachment_list )
|
||||
{
|
||||
if( attachment == "lasersight" )
|
||||
{
|
||||
has_laser = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handling for EMP
|
||||
|
||||
|
||||
while( self isEMPed() && has_laser)
|
||||
{
|
||||
wait( 0.05);
|
||||
self LaserOff();
|
||||
self.wasEMP = true;
|
||||
continue;
|
||||
}
|
||||
if ( self.wasEMP && has_laser)
|
||||
{
|
||||
self.wasEMP = false;
|
||||
if( self maps\mp\gametypes\_class::isExoXMG( self GetCurrentWeapon() ) || self maps\mp\gametypes\_class::isSac3( self GetCurrentWeapon() ) )
|
||||
self LaserOn( "mp_attachment_lasersight_short" );
|
||||
else
|
||||
self LaserOn( "mp_attachment_lasersight" );
|
||||
}
|
||||
|
||||
|
||||
// Adding a special case for the LSR launcher. It should have a laser on even though it doesn't have a laser attachment.
|
||||
if ( IsSubStr( self GetCurrentWeapon(), "maaws" ) )
|
||||
{
|
||||
has_laser = true;
|
||||
}
|
||||
|
||||
if ( has_laser && self IsThrowingGrenade() )
|
||||
{
|
||||
if( isDefined( self.laser_on ) && self.laser_on )
|
||||
{
|
||||
self LaserOff();
|
||||
self.laser_on = false;
|
||||
|
||||
// god help us, all of this is required to get the timing correct...
|
||||
|
||||
// IsUsingOffhand() turns on the moment the viewmodel starts holding the grenade weapon
|
||||
// IsThrowingGrenade() is here to ensure we don't get stuck if the player cancels the grenade process
|
||||
while ( !self IsUsingOffhand() && self IsThrowingGrenade() )
|
||||
wait( 0.05 );
|
||||
|
||||
// IsUsingOffhand() turns off the moment the viewmodel starts holding the primary weapon again
|
||||
// IsThrowingGrenade() is here to ensure we don't get stuck if the player cancels the grenade process
|
||||
while ( self IsUsingOffhand() && self IsThrowingGrenade() )
|
||||
wait( 0.05 );
|
||||
|
||||
// IsThrowingGrenade() turns off the moment the primary weapon is usable again
|
||||
while ( self IsThrowingGrenade() )
|
||||
wait( 0.05 );
|
||||
|
||||
if( self maps\mp\gametypes\_class::isExoXMG( self GetCurrentWeapon() ) || self maps\mp\gametypes\_class::isSac3( self GetCurrentWeapon() ) )
|
||||
self LaserOn( "mp_attachment_lasersight_short" );
|
||||
else
|
||||
self LaserOn( "mp_attachment_lasersight" );
|
||||
|
||||
self.laser_on = true;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( !has_laser )
|
||||
{
|
||||
if( isDefined( self.laser_on ) && self.laser_on )
|
||||
{
|
||||
self LaserOff();
|
||||
self.laser_on = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !isDefined( self.laser_on ) || !self.laser_on )
|
||||
{
|
||||
if( self maps\mp\gametypes\_class::isExoXMG( self GetCurrentWeapon() ) || self maps\mp\gametypes\_class::isSac3( self GetCurrentWeapon() ) )
|
||||
self LaserOn( "mp_attachment_lasersight_short" );
|
||||
else
|
||||
self LaserOn( "mp_attachment_lasersight" );
|
||||
|
||||
self.laser_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
499
raw/maps/mp/_load.gsc
Normal file
499
raw/maps/mp/_load.gsc
Normal file
@ -0,0 +1,499 @@
|
||||
#include common_scripts\utility;
|
||||
#include common_scripts\_fx;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
if ( isDefined( level._loadStarted ) )
|
||||
return;
|
||||
|
||||
level._loadStarted = true;
|
||||
level.virtualLobbyActive = GetDvarInt( "virtualLobbyActive", 0 );
|
||||
|
||||
set_console_status();
|
||||
level.createFX_enabled = ( getdvar( "createfx" ) != "" );
|
||||
|
||||
struct_class_init();
|
||||
|
||||
initGameFlags();
|
||||
initLevelFlags();
|
||||
initGlobals();
|
||||
|
||||
level.generic_index = 0;
|
||||
// flag_struct is used as a placeholder when a flag is set without an entity
|
||||
|
||||
level.flag_struct = spawnstruct();
|
||||
level.flag_struct assign_unique_id();
|
||||
if ( !isdefined( level.flag ) )
|
||||
{
|
||||
level.flag = [];
|
||||
level.flags_lock = [];
|
||||
}
|
||||
|
||||
level.requiredMapAspectRatio = getDvarFloat( "scr_RequiredMapAspectratio", 1 );
|
||||
level.createClientFontString_func = maps\mp\gametypes\_hud_util::createFontString;
|
||||
level.HUDsetPoint_func = maps\mp\gametypes\_hud_util::setPoint;
|
||||
level.leaderDialogOnPlayer_func = maps\mp\_utility::leaderDialogOnPlayer;
|
||||
|
||||
thread maps\mp\gametypes\_tweakables::init();
|
||||
|
||||
|
||||
if ( !isdefined( level.func ) )
|
||||
level.func = [];
|
||||
level.func[ "precacheMpAnim" ] = ::precacheMpAnim;
|
||||
level.func[ "scriptModelPlayAnim" ] = ::scriptModelPlayAnim;
|
||||
level.func[ "scriptModelClearAnim" ] = ::scriptModelClearAnim;
|
||||
|
||||
// dodge this stuff for createfx tool.
|
||||
if( ! level.createFX_enabled )
|
||||
{
|
||||
thread maps\mp\_movers::init();
|
||||
thread maps\mp\_shutter::main();
|
||||
thread maps\mp\_destructables::init();
|
||||
thread common_scripts\_elevator::init();
|
||||
thread maps\mp\_dynamic_world::init();
|
||||
thread common_scripts\_destructible::init();
|
||||
}
|
||||
|
||||
game["thermal_vision"] = "default";
|
||||
|
||||
VisionSetNaked( "", 0 ); // go to default visionset
|
||||
VisionSetNight( "default_night_mp" );
|
||||
VisionSetMissilecam( "orbital_strike" );
|
||||
VisionSetThermal( game[ "thermal_vision" ] );
|
||||
VisionSetPain( "near_death_mp", 0 );
|
||||
|
||||
lanterns = getentarray("lantern_glowFX_origin","targetname");
|
||||
for( i = 0 ; i < lanterns.size ; i++ )
|
||||
lanterns[i] thread lanterns();
|
||||
|
||||
maps\mp\_audio::init_audio();
|
||||
maps\mp\_art::main();
|
||||
|
||||
setupExploders();
|
||||
|
||||
thread common_scripts\_fx::initFX();
|
||||
if ( level.createFX_enabled )
|
||||
{
|
||||
maps\mp\gametypes\_spawnlogic::setMapCenterForDev();
|
||||
maps\mp\_createfx::createfx();
|
||||
}
|
||||
|
||||
if ( getdvar( "r_reflectionProbeGenerate" ) == "1" )
|
||||
{
|
||||
deleteDuringReflectionProbeGeneration();
|
||||
maps\mp\gametypes\_spawnlogic::setMapCenterForDev();
|
||||
maps\mp\_global_fx::main();
|
||||
level waittill( "eternity" );
|
||||
}
|
||||
|
||||
thread maps\mp\_global_fx::main();
|
||||
|
||||
// Do various things on triggers
|
||||
for ( p = 0;p < 6;p ++ )
|
||||
{
|
||||
switch( p )
|
||||
{
|
||||
case 0:
|
||||
triggertype = "trigger_multiple";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
triggertype = "trigger_once";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
triggertype = "trigger_use";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
triggertype = "trigger_radius";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
triggertype = "trigger_lookat";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( p == 5 );
|
||||
triggertype = "trigger_damage";
|
||||
break;
|
||||
}
|
||||
|
||||
triggers = getentarray( triggertype, "classname" );
|
||||
|
||||
for ( i = 0;i < triggers.size;i ++ )
|
||||
{
|
||||
if( isdefined( triggers[ i ].script_prefab_exploder) )
|
||||
triggers[i].script_exploder = triggers[ i ].script_prefab_exploder;
|
||||
|
||||
if( isdefined( triggers[ i ].script_exploder) )
|
||||
level thread maps\mp\_load::exploder_load( triggers[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
hurtTriggers = getentarray( "trigger_hurt", "classname" );
|
||||
|
||||
foreach ( hurtTrigger in hurtTriggers )
|
||||
{
|
||||
hurtTrigger thread hurtPlayersThink();
|
||||
}
|
||||
|
||||
thread maps\mp\_animatedmodels::main();
|
||||
|
||||
// auto-sentry
|
||||
level.func[ "damagefeedback" ] = maps\mp\gametypes\_damagefeedback::updateDamageFeedback;
|
||||
level.func[ "setTeamHeadIcon" ] = maps\mp\_entityheadicons::setTeamHeadIcon;
|
||||
level.laserOn_func = ::laserOn;
|
||||
level.laserOff_func = ::laserOff;
|
||||
|
||||
level.connectPathsFunction = ::connectPaths;
|
||||
level.disconnectPathsFunction = ::disconnectPaths;
|
||||
|
||||
// defaults
|
||||
setDvar( "sm_spotLightScoreModelScale", 0.1 );
|
||||
setDvar( "r_specularcolorscale", 2.5 );
|
||||
setDvar( "r_diffusecolorscale", 1 );
|
||||
setDvar( "r_lightGridEnableTweaks", 0 );
|
||||
setDvar( "r_lightGridIntensity", 1 );
|
||||
setDvar( "r_lightGridContrast", 0 );
|
||||
setDvar( "r_dof_physical_enable", 1 );
|
||||
setdvar( "r_volumeLightScatter", 0 );
|
||||
setDvar( "r_uiblurdstmode", 0 );
|
||||
setDvar( "r_blurdstgaussianblurradius", 1 );
|
||||
setDvar( "r_dof_physical_bokehEnable", 0 );
|
||||
if ( level.nextgen )
|
||||
{
|
||||
// Reset polygon offset preset to SM_POLYGON_OFFSET_PRESET_DEFAULT
|
||||
setDvar( "sm_polygonOffsetPreset", 0 );
|
||||
}
|
||||
|
||||
// setup kill cam ents on destructibles
|
||||
setupDestructibleKillCamEnts();
|
||||
|
||||
// precache the bomb site weapon
|
||||
if ( level.virtualLobbyActive == 0 )
|
||||
PreCacheItem( "bomb_site_mp" );
|
||||
|
||||
level.fauxVehicleCount = 0; // this keeps an artificial count so we can check airspace and vehicles limits preemptively
|
||||
|
||||
load_costume_indices();
|
||||
/#
|
||||
// when host migration happens, all of the dev dvars get cleared, so this will reset them all
|
||||
level thread reInitializeDevDvarsOnMigration();
|
||||
#/
|
||||
}
|
||||
|
||||
exploder_load( trigger )
|
||||
{
|
||||
level endon( "killexplodertridgers" + trigger.script_exploder );
|
||||
trigger waittill( "trigger" );
|
||||
if ( isdefined( trigger.script_chance ) && randomfloat( 1 ) > trigger.script_chance )
|
||||
{
|
||||
if ( isdefined( trigger.script_delay ) )
|
||||
wait trigger.script_delay;
|
||||
else
|
||||
wait 4;
|
||||
level thread exploder_load( trigger );
|
||||
return;
|
||||
}
|
||||
common_scripts\_exploder::exploder( trigger.script_exploder );
|
||||
level notify( "killexplodertridgers" + trigger.script_exploder );
|
||||
}
|
||||
|
||||
|
||||
setupExploders()
|
||||
{
|
||||
// Hide exploder models.
|
||||
ents = getentarray( "script_brushmodel", "classname" );
|
||||
smodels = getentarray( "script_model", "classname" );
|
||||
for ( i = 0;i < smodels.size;i ++ )
|
||||
ents[ ents.size ] = smodels[ i ];
|
||||
|
||||
for ( i = 0;i < ents.size;i ++ )
|
||||
{
|
||||
if ( isdefined( ents[ i ].script_prefab_exploder ) )
|
||||
ents[ i ].script_exploder = ents[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( ents[ i ].script_exploder ) )
|
||||
{
|
||||
if ( ( ents[ i ].model == "fx" ) && ( ( !isdefined( ents[ i ].targetname ) ) || ( ents[ i ].targetname != "exploderchunk" ) ) )
|
||||
ents[ i ] hide();
|
||||
else if ( ( isdefined( ents[ i ].targetname ) ) && ( ents[ i ].targetname == "exploder" ) )
|
||||
{
|
||||
ents[ i ] hide();
|
||||
ents[ i ] notsolid();
|
||||
//if ( isdefined( ents[ i ].script_disconnectpaths ) )
|
||||
//ents[ i ] connectpaths();
|
||||
}
|
||||
else if ( ( isdefined( ents[ i ].targetname ) ) && ( ents[ i ].targetname == "exploderchunk" ) )
|
||||
{
|
||||
ents[ i ] hide();
|
||||
ents[ i ] notsolid();
|
||||
//if ( isdefined( ents[ i ].spawnflags ) && ( ents[ i ].spawnflags & 1 ) )
|
||||
//ents[ i ] connectpaths();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script_exploders = [];
|
||||
|
||||
potentialExploders = getentarray( "script_brushmodel", "classname" );
|
||||
for ( i = 0;i < potentialExploders.size;i ++ )
|
||||
{
|
||||
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
|
||||
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( potentialExploders[ i ].script_exploder ) )
|
||||
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
|
||||
}
|
||||
|
||||
potentialExploders = getentarray( "script_model", "classname" );
|
||||
for ( i = 0;i < potentialExploders.size;i ++ )
|
||||
{
|
||||
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
|
||||
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( potentialExploders[ i ].script_exploder ) )
|
||||
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
|
||||
}
|
||||
|
||||
potentialExploders = getentarray( "item_health", "classname" );
|
||||
for ( i = 0;i < potentialExploders.size;i ++ )
|
||||
{
|
||||
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
|
||||
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( potentialExploders[ i ].script_exploder ) )
|
||||
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
|
||||
}
|
||||
|
||||
if ( !isdefined( level.createFXent ) )
|
||||
level.createFXent = [];
|
||||
|
||||
acceptableTargetnames = [];
|
||||
acceptableTargetnames[ "exploderchunk visible" ] = true;
|
||||
acceptableTargetnames[ "exploderchunk" ] = true;
|
||||
acceptableTargetnames[ "exploder" ] = true;
|
||||
|
||||
for ( i = 0; i < script_exploders.size; i ++ )
|
||||
{
|
||||
exploder = script_exploders[ i ];
|
||||
ent = createExploder( exploder.script_fxid );
|
||||
ent.v = [];
|
||||
ent.v[ "origin" ] = exploder.origin;
|
||||
ent.v[ "angles" ] = exploder.angles;
|
||||
ent.v[ "delay" ] = exploder.script_delay;
|
||||
ent.v[ "firefx" ] = exploder.script_firefx;
|
||||
ent.v[ "firefxdelay" ] = exploder.script_firefxdelay;
|
||||
ent.v[ "firefxsound" ] = exploder.script_firefxsound;
|
||||
ent.v[ "firefxtimeout" ] = exploder.script_firefxtimeout;
|
||||
ent.v[ "earthquake" ] = exploder.script_earthquake;
|
||||
ent.v[ "damage" ] = exploder.script_damage;
|
||||
ent.v[ "damage_radius" ] = exploder.script_radius;
|
||||
ent.v[ "soundalias" ] = exploder.script_soundalias;
|
||||
ent.v[ "repeat" ] = exploder.script_repeat;
|
||||
ent.v[ "delay_min" ] = exploder.script_delay_min;
|
||||
ent.v[ "delay_max" ] = exploder.script_delay_max;
|
||||
ent.v[ "target" ] = exploder.target;
|
||||
ent.v[ "ender" ] = exploder.script_ender;
|
||||
ent.v[ "type" ] = "exploder";
|
||||
// ent.v[ "worldfx" ] = true;
|
||||
if ( !isdefined( exploder.script_fxid ) )
|
||||
ent.v[ "fxid" ] = "No FX";
|
||||
else
|
||||
ent.v[ "fxid" ] = exploder.script_fxid;
|
||||
ent.v[ "exploder" ] = exploder.script_exploder;
|
||||
assertEx( isdefined( exploder.script_exploder ), "Exploder at origin " + exploder.origin + " has no script_exploder" );
|
||||
|
||||
if ( !isdefined( ent.v[ "delay" ] ) )
|
||||
ent.v[ "delay" ] = 0;
|
||||
|
||||
if ( isdefined( exploder.target ) )
|
||||
{
|
||||
get_ent = GetEntArray( ent.v[ "target" ], "targetname" )[ 0 ];
|
||||
if ( IsDefined( get_ent ) )
|
||||
{
|
||||
org = get_ent.origin;
|
||||
ent.v[ "angles" ] = VectorToAngles( org - ent.v[ "origin" ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
get_ent = get_target_ent( ent.v[ "target" ] );
|
||||
if ( IsDefined( get_ent ) )
|
||||
{
|
||||
org = get_ent.origin;
|
||||
ent.v[ "angles" ] = VectorToAngles( org - ent.v[ "origin" ] );
|
||||
}
|
||||
}
|
||||
// forward = anglestoforward( angles );
|
||||
// up = anglestoup( angles );
|
||||
}
|
||||
|
||||
// this basically determines if its a brush / model exploder or not
|
||||
if ( exploder.classname == "script_brushmodel" || isdefined( exploder.model ) )
|
||||
{
|
||||
ent.model = exploder;
|
||||
ent.model.disconnect_paths = exploder.script_disconnectpaths;
|
||||
}
|
||||
|
||||
if ( isdefined( exploder.targetname ) && isdefined( acceptableTargetnames[ exploder.targetname ] ) )
|
||||
ent.v[ "exploder_type" ] = exploder.targetname;
|
||||
else
|
||||
ent.v[ "exploder_type" ] = "normal";
|
||||
|
||||
ent common_scripts\_createfx::post_entity_creation_function();
|
||||
}
|
||||
}
|
||||
|
||||
lanterns()
|
||||
{
|
||||
AssertEx ( (isdefined(level._effect["lantern_light"])), "No \"lantern_light\" fx defined in level." );
|
||||
loopfx("lantern_light", self.origin, 0.3, self.origin + (0,0,1));
|
||||
}
|
||||
|
||||
|
||||
hurtPlayersThink()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
wait ( randomFloat( 1.0 ) );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player isTouching( self ) && isReallyAlive( player ) )
|
||||
player _suicide();
|
||||
}
|
||||
|
||||
wait ( 0.5 );
|
||||
}
|
||||
}
|
||||
|
||||
setupDestructibleKillCamEnts()
|
||||
{
|
||||
destructible_vehicles = GetEntArray( "destructible_vehicle", "targetname" );
|
||||
foreach( dest in destructible_vehicles )
|
||||
{
|
||||
// TU0: taking out some of these because they aren't in the playable area
|
||||
switch( GetDvar( "mapname" ) )
|
||||
{
|
||||
case "mp_interchange":
|
||||
// don't need a killcam ent if we are above this because we aren't in the playable area
|
||||
if( dest.origin[2] > 150.0 )
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
bulletStart = dest.origin + ( 0, 0, 5 );
|
||||
bulletEnd = ( dest.origin + ( 0, 0, 128 ) );
|
||||
result = BulletTrace( bulletStart, bulletEnd, false, dest );
|
||||
dest.killCamEnt = Spawn( "script_model", result[ "position" ] );
|
||||
dest.killCamEnt.targetname = "killCamEnt_destructible_vehicle";
|
||||
dest.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
dest thread deleteDestructibleKillCamEnt();
|
||||
}
|
||||
|
||||
destructible_toys = GetEntArray( "destructible_toy", "targetname" );
|
||||
foreach( dest in destructible_toys )
|
||||
{
|
||||
bulletStart = dest.origin + ( 0, 0, 5 );
|
||||
bulletEnd = ( dest.origin + ( 0, 0, 128 ) );
|
||||
result = BulletTrace( bulletStart, bulletEnd, false, dest );
|
||||
dest.killCamEnt = Spawn( "script_model", result[ "position" ] );
|
||||
dest.killCamEnt.targetname = "killCamEnt_destructible_toy";
|
||||
dest.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
dest thread deleteDestructibleKillCamEnt();
|
||||
}
|
||||
|
||||
explodable_barrels = GetEntArray( "explodable_barrel", "targetname" );
|
||||
foreach( dest in explodable_barrels )
|
||||
{
|
||||
bulletStart = dest.origin + ( 0, 0, 5 );
|
||||
bulletEnd = ( dest.origin + ( 0, 0, 128 ) );
|
||||
result = BulletTrace( bulletStart, bulletEnd, false, dest );
|
||||
dest.killCamEnt = Spawn( "script_model", result[ "position" ] );
|
||||
dest.killCamEnt.targetname = "killCamEnt_explodable_barrel";
|
||||
dest.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
dest thread deleteDestructibleKillCamEnt();
|
||||
}
|
||||
|
||||
// ADD MORE DESTRUCTIBLES HERE IF APPLICABLE
|
||||
}
|
||||
|
||||
deleteDestructibleKillCamEnt()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
killCamEnt = self.killCamEnt;
|
||||
killCamEnt endon( "death" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
wait( 10 );
|
||||
if( IsDefined( killCamEnt ) )
|
||||
killCamEnt delete();
|
||||
}
|
||||
|
||||
deleteDuringReflectionProbeGeneration()
|
||||
{
|
||||
ents = GetEntArray( "boost_jump_border", "targetname" );
|
||||
foreach ( ent in ents )
|
||||
ent delete();
|
||||
|
||||
ents = GetEntArray( "mp_recovery_signage", "targetname" );
|
||||
foreach ( ent in ents )
|
||||
ent delete();
|
||||
|
||||
hordeDomes = GetEntArray ( "horde_dome", "targetname" );
|
||||
foreach ( hordeDome in hordeDomes )
|
||||
hordeDome delete();
|
||||
|
||||
zones = GetEntArray( "hp_zone_center", "targetname" );
|
||||
foreach ( zone in zones )
|
||||
{
|
||||
if ( isdefined( zone.target ) )
|
||||
{
|
||||
ents = GetEntArray( zone.target, "targetname" );
|
||||
foreach ( ent in ents )
|
||||
ent delete();
|
||||
}
|
||||
}
|
||||
|
||||
orbital_overlay_ents = GetEntArray( "orbital_bad_spawn_overlay", "targetname" );
|
||||
foreach ( overlay in orbital_overlay_ents )
|
||||
overlay delete();
|
||||
}
|
||||
|
||||
load_costume_indices()
|
||||
{
|
||||
|
||||
if ( IsDefined( level.costumeCategories ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// this has to match the native categories: eventually remove script handling of costume models
|
||||
level.costumeCategories = [
|
||||
"gender",
|
||||
"shirt",
|
||||
"head",
|
||||
"pants",
|
||||
"gloves",
|
||||
"shoes",
|
||||
"kneepads",
|
||||
"gear",
|
||||
"hat",
|
||||
"eyewear",
|
||||
"exo"
|
||||
];
|
||||
|
||||
level.costumeCat2Idx = [];
|
||||
for ( i = 0; i < level.costumeCategories.size; i++ )
|
||||
{
|
||||
category = level.costumeCategories[i];
|
||||
level.costumeCat2Idx[category] = i;
|
||||
}
|
||||
}
|
||||
|
71
raw/maps/mp/_lsrmissileguidance.gsc
Normal file
71
raw/maps/mp/_lsrmissileguidance.gsc
Normal file
@ -0,0 +1,71 @@
|
||||
CONST_lsr_weaponname = "maaws";
|
||||
|
||||
|
||||
monitor_lsr_missile_launch() // self = player
|
||||
{
|
||||
Assert(IsPlayer(self) || IsAgent(self));
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "missile_fire", projectile, weaponName );
|
||||
if( IsSubStr( weaponName, CONST_lsr_weaponname ) )
|
||||
{
|
||||
if ( !IsDefined( self.lsr_target_ent ) )
|
||||
{
|
||||
self.lsr_target_ent = Spawn( "script_origin", self.origin );
|
||||
|
||||
// giving targetname to track bandwidth
|
||||
self.lsr_target_ent.targetname = "lsr_missile";
|
||||
}
|
||||
|
||||
self.lsr_target_ent thread lsr_target_monitor_and_cleanup( projectile );
|
||||
projectile thread lsr_rocket_think( self );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lsr_rocket_think( firing_player ) // self = lsr rocket
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
firing_player endon( "death" );
|
||||
firing_player endon( "disconnect" );
|
||||
firing_player endon( "faux_spawn" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( firing_player PlayerAds() > 0.3 )
|
||||
{
|
||||
firing_player_forward = AnglesToForward( firing_player GetPlayerAngles() );
|
||||
firing_player_eye = firing_player GetEye();
|
||||
firing_player_eye_lookat = firing_player_eye + firing_player_forward * 15000;
|
||||
|
||||
traceinfo = BulletTrace( firing_player_eye, firing_player_eye_lookat, true, firing_player, true, false, false, false, false );
|
||||
firing_player.lsr_target_ent.origin = traceinfo[ "position" ];
|
||||
self Missile_SetTargetEnt( firing_player.lsr_target_ent );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lsr_target_monitor_and_cleanup( projectile )
|
||||
{
|
||||
if ( !IsDefined( self.lsr_rocket_count ) )
|
||||
self.lsr_rocket_count = 1;
|
||||
else
|
||||
self.lsr_rocket_count++;
|
||||
|
||||
projectile waittill( "death" );
|
||||
self.lsr_rocket_count--;
|
||||
|
||||
if ( self.lsr_rocket_count == 0 )
|
||||
{
|
||||
self Delete();
|
||||
}
|
||||
}
|
1721
raw/maps/mp/_matchdata.gsc
Normal file
1721
raw/maps/mp/_matchdata.gsc
Normal file
File diff suppressed because it is too large
Load Diff
3
raw/maps/mp/_menus.gsc
Normal file
3
raw/maps/mp/_menus.gsc
Normal file
@ -0,0 +1,3 @@
|
||||
init()
|
||||
{
|
||||
}
|
356
raw/maps/mp/_microdronelauncher.gsc
Normal file
356
raw/maps/mp/_microdronelauncher.gsc
Normal file
@ -0,0 +1,356 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
CONST_microdrone_weaponname = "iw5_microdronelauncher_mp";
|
||||
CONST_max_homing_angle_from_grenade = 15;
|
||||
CONST_begin_homing_time = .35;
|
||||
CONST_drop_accel = 800; // inches/s^2
|
||||
|
||||
CONST_explosion_delay_time = 3;
|
||||
|
||||
|
||||
monitor_microdrone_launch()
|
||||
{
|
||||
level._effect[ "mdl_sticky_explosion" ] = LoadFx( "vfx/explosion/frag_grenade_default" );
|
||||
level._effect[ "mdl_sticky_blinking" ] = LoadFx( "vfx/lights/light_semtex_blinking" );
|
||||
|
||||
Assert(IsPlayer(self) || IsAgent(self));
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
while(true)
|
||||
{
|
||||
self waittill("missile_fire", projectile, weaponName);
|
||||
if( IsSubStr( weaponName, CONST_microdrone_weaponname ) )
|
||||
{
|
||||
projectile setOtherEnt( self );
|
||||
// //projectile thread microdrone_think(self); //tracking functionality, disabling to test new delayed sticky explosives
|
||||
// projectile thread determine_projectile_position( self );
|
||||
// projectile waittill( "death" );
|
||||
// projectile thread determine_sticky_position( self );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
determine_projectile_position( firing_player )
|
||||
{
|
||||
//keep track of where it was the frame before
|
||||
//when it impacts, do a trace from the frame prior to the impact location to get all your info
|
||||
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
if( !IsDefined( self.previous_position ) )
|
||||
{
|
||||
self.previous_position = self.origin;
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
self.previous_position = self.origin;
|
||||
}
|
||||
}
|
||||
|
||||
determine_sticky_position( firing_player )
|
||||
{
|
||||
firing_player endon( "spawned_player" );
|
||||
|
||||
if( !IsDefined( self.previous_position ) )
|
||||
return;
|
||||
|
||||
if( !isDefined( self ) ) //timed out
|
||||
return;
|
||||
|
||||
previous_to_current = self.origin - self.previous_position;
|
||||
trajectory = VectorToAngles( previous_to_current );
|
||||
forward = AnglesToForward( trajectory ) * 8000;
|
||||
end = self.origin + forward;
|
||||
|
||||
trace = BulletTrace( self.previous_position, end, true, firing_player, true, true );
|
||||
|
||||
if( trace[ "fraction" ] < 1 && isDefined( trace[ "position" ] ) )
|
||||
{
|
||||
sticky_grenade = Spawn( "script_model", trace[ "position" ] );
|
||||
sticky_grenade SetModel( "projectile_semtex_grenade" );
|
||||
|
||||
if( isDefined( trace[ "entity" ] ) )
|
||||
{
|
||||
if( IsPlayer( trace[ "entity" ] ) )
|
||||
{
|
||||
firing_player thread show_stuck_fanfare();
|
||||
trace[ "entity" ] thread show_stuck_fanfare();
|
||||
}
|
||||
|
||||
sticky_grenade LinkTo( trace[ "entity" ] );
|
||||
}
|
||||
|
||||
sticky_grenade thread sticky_timer( firing_player );
|
||||
sticky_grenade thread sticky_fx( firing_player );
|
||||
sticky_grenade thread remove_sticky( firing_player );
|
||||
}
|
||||
}
|
||||
|
||||
microdronelauncher_cleanup()
|
||||
{
|
||||
self waittill_any( "death", "disconnect", "faux_spawn" );
|
||||
|
||||
if ( IsDefined( self.HUDItem ) )
|
||||
{
|
||||
foreach ( hudItem in self.HUDItem)
|
||||
hudItem Destroy();
|
||||
self.HUDItem = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
show_stuck_fanfare()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
if( !IsDefined( self.HUDItem ) )
|
||||
{
|
||||
self.HUDItem = [];
|
||||
}
|
||||
|
||||
if( IsDefined( self.HUDItem ) && !IsDefined( self.HUDItem[ "mdlStuckText" ] ) )
|
||||
{
|
||||
self.HUDItem[ "mdlStuckText" ] = NewClientHudElem( self );
|
||||
self.HUDItem[ "mdlStuckText" ].x = 0;
|
||||
self.HUDItem[ "mdlStuckText" ].y = -170;
|
||||
self.HUDItem[ "mdlStuckText" ].alignX = "center";
|
||||
self.HUDItem[ "mdlStuckText" ].alignY = "middle";
|
||||
self.HUDItem[ "mdlStuckText" ].horzAlign = "center";
|
||||
self.HUDItem[ "mdlStuckText" ].vertAlign = "middle";
|
||||
self.HUDItem[ "mdlStuckText" ].fontScale = 3.0;
|
||||
self.HUDItem[ "mdlStuckText" ].alpha = 0.0;
|
||||
self.HUDItem[ "mdlStuckText" ] SetText( "STUCK!" );
|
||||
self thread microdronelauncher_cleanup();
|
||||
}
|
||||
|
||||
if( IsDefined( self.HUDItem[ "mdlStuckText" ] ) )
|
||||
{
|
||||
self.HUDItem[ "mdlStuckText" ].alpha = 1.0;
|
||||
wait( 2.0 );
|
||||
self.HUDItem[ "mdlStuckText" ].alpha = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
sticky_timer( firing_player )
|
||||
{
|
||||
firing_player endon( "spawned_player" );
|
||||
|
||||
wait( CONST_explosion_delay_time );
|
||||
|
||||
PlayFx( getfx( "mdl_sticky_explosion" ), self.origin );
|
||||
PhysicsExplosionSphere( self.origin, 256, 32, 1.0 );
|
||||
RadiusDamage( self.origin, 256, 130, 15, firing_player, "MOD_EXPLOSIVE", "iw5_microdronelauncher_mp" );
|
||||
|
||||
self notify( "exploded" );
|
||||
}
|
||||
|
||||
sticky_fx( firing_player )
|
||||
{
|
||||
firing_player endon( "spawned_player" );
|
||||
self endon( "exploded" );
|
||||
|
||||
self.fx_origin = spawn_tag_origin();
|
||||
self.fx_origin.origin = self.origin;
|
||||
self.fx_origin Show();
|
||||
|
||||
wait( 0.1 );
|
||||
|
||||
PlayFXOnTag( getfx( "mdl_sticky_blinking" ), self.fx_origin, "tag_origin" );
|
||||
}
|
||||
|
||||
remove_sticky( firing_player )
|
||||
{
|
||||
self thread remove_sticky_on_explosion( firing_player );
|
||||
self thread remove_sticky_on_respawn( firing_player );
|
||||
}
|
||||
|
||||
remove_sticky_on_explosion( firing_player )
|
||||
{
|
||||
firing_player endon( "spawned_player" );
|
||||
|
||||
self waittill( "exploded" );
|
||||
|
||||
if( isDefined( self ) )
|
||||
{
|
||||
self cleanup_sticky();
|
||||
}
|
||||
}
|
||||
|
||||
remove_sticky_on_respawn( firing_player )
|
||||
{
|
||||
self endon( "exploded" );
|
||||
|
||||
firing_player waittill( "spawned_player" );
|
||||
|
||||
if( isDefined( self ) )
|
||||
{
|
||||
self cleanup_sticky();
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_sticky()
|
||||
{
|
||||
StopFXOnTag( getfx( "mdl_sticky_blinking" ), self.fx_origin, "tag_origin" );
|
||||
self Delete();
|
||||
}
|
||||
|
||||
microdrone_think(firing_player)
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
firing_player endon( "death" );
|
||||
firing_player endon( "disconnect" );
|
||||
firing_player endon( "faux_spawn" );
|
||||
|
||||
start_origin = self.origin;
|
||||
|
||||
// seems missiles don't move on the first frame, so need to wait 2 frames to get the velocity
|
||||
self get_differentiated_velocity();
|
||||
wait .05;
|
||||
self get_differentiated_velocity();
|
||||
wait .05;
|
||||
elapsed_time = .1;
|
||||
|
||||
init_vel = self get_differentiated_velocity();
|
||||
while(true)
|
||||
{
|
||||
// must call every frame to get accurate results
|
||||
cur_vel = self get_differentiated_velocity();
|
||||
|
||||
set_target = false;
|
||||
if(elapsed_time >= CONST_begin_homing_time)
|
||||
{
|
||||
best_target = microdrone_get_best_target(start_origin, VectorNormalize(init_vel), cur_vel, firing_player);
|
||||
if(IsDefined(best_target))
|
||||
{
|
||||
self Missile_SetTargetEnt(best_target, microdrone_get_target_offset(best_target));
|
||||
set_target = true;
|
||||
|
||||
// so that if we lose the lock, we will start dropping from our current velocity
|
||||
init_vel = cur_vel;
|
||||
|
||||
/#
|
||||
//Line(self.origin, best_target.origin + microdrone_get_target_offset(best_target), (0, 1, 0));
|
||||
#/
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/#
|
||||
//Line(self.origin, self.origin + (0, 0, 100), (0, 1, 0));
|
||||
#/
|
||||
}
|
||||
|
||||
if(!set_target)
|
||||
{
|
||||
// simulate drop. We could do this in projectile steering code (and multiplayer would want to do that), but this is simpler.
|
||||
desired_dir = VectorNormalize(init_vel + (0, 0, -.5 * CONST_drop_accel * Squared(elapsed_time)));
|
||||
self Missile_SetTargetPos(self.origin + desired_dir * 10000);
|
||||
}
|
||||
|
||||
wait .05;
|
||||
elapsed_time += .05;
|
||||
}
|
||||
}
|
||||
|
||||
microdrone_get_best_target(start_origin, dir, cur_vel, firing_player)
|
||||
{
|
||||
min_dot_from_grenade = Cos(CONST_max_homing_angle_from_grenade);
|
||||
|
||||
best_target = undefined;
|
||||
best_target_dot = Cos(CONST_max_homing_angle_from_grenade);
|
||||
foreach( target in level.players )
|
||||
{
|
||||
if( target == firing_player )
|
||||
continue;
|
||||
|
||||
if( target.team == firing_player.team )
|
||||
continue;
|
||||
|
||||
target_pos = microdrone_get_target_pos(target);
|
||||
dot = VectorDot(VectorNormalize(cur_vel), VectorNormalize(target_pos - self.origin));
|
||||
if(dot > best_target_dot)
|
||||
{
|
||||
|
||||
if(BulletTracePassed(self.origin, target_pos, false, target))
|
||||
{
|
||||
best_target = target;
|
||||
best_target_dot = dot;
|
||||
}
|
||||
else
|
||||
{
|
||||
/#
|
||||
//result = BulletTrace(self.origin, target_pos, false, target);
|
||||
//Assert(!IsDefined(result["entity"]) || result["entity"] != target);
|
||||
//line(self.origin, result["position"], (1, 0, 0));
|
||||
#/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_target;
|
||||
}
|
||||
|
||||
is_enemy_target(target, firing_player)
|
||||
{
|
||||
team = undefined;
|
||||
|
||||
if(IsAi(target))
|
||||
team = target.team;
|
||||
else if(IsDefined(target.script_team))
|
||||
team = target.script_team;
|
||||
|
||||
return IsEnemyTeam(team, firing_player.team);
|
||||
}
|
||||
|
||||
microdrone_get_target_pos(target)
|
||||
{
|
||||
return target GetPointInBounds(0, 0, 0);
|
||||
}
|
||||
|
||||
microdrone_get_target_offset(target)
|
||||
{
|
||||
return microdrone_get_target_pos(target) - target.origin;
|
||||
}
|
||||
|
||||
get_differentiated_velocity()
|
||||
{
|
||||
self differentiate_motion();
|
||||
return self.differentiated_velocity;
|
||||
}
|
||||
|
||||
differentiate_motion()
|
||||
{
|
||||
time = GetTime() * .001;
|
||||
|
||||
if(!IsDefined(self.differentiated_last_update))
|
||||
{
|
||||
self.differentiated_last_update = time;
|
||||
self.differentiated_last_origin = self.origin;
|
||||
self.differentiated_last_velocity = (0, 0, 0);
|
||||
self.differentiated_last_acceleration = (0, 0, 0);
|
||||
self.differentiated_jerk = (0, 0, 0);
|
||||
self.differentiated_acceleration = (0, 0, 0);
|
||||
self.differentiated_velocity = (0, 0, 0);
|
||||
self.differentiated_speed = 0;
|
||||
}
|
||||
else if(self.differentiated_last_update != time)
|
||||
{
|
||||
dt = time - self.differentiated_last_update;
|
||||
self.differentiated_last_update = time;
|
||||
self.differentiated_jerk = (self.differentiated_acceleration - self.differentiated_last_acceleration) / dt;
|
||||
self.differentiated_last_acceleration = self.differentiated_acceleration;
|
||||
self.differentiated_acceleration = (self.differentiated_velocity - self.differentiated_last_velocity) / dt;
|
||||
self.differentiated_last_velocity = self.differentiated_velocity;
|
||||
self.differentiated_velocity = (self.origin - self.differentiated_last_origin) / dt;
|
||||
self.differentiated_last_origin = self.origin;
|
||||
self.differentiated_speed = Length(self.differentiated_velocity);
|
||||
}
|
||||
}
|
1396
raw/maps/mp/_movers.gsc
Normal file
1396
raw/maps/mp/_movers.gsc
Normal file
File diff suppressed because it is too large
Load Diff
613
raw/maps/mp/_mp_lights.gsc
Normal file
613
raw/maps/mp/_mp_lights.gsc
Normal file
@ -0,0 +1,613 @@
|
||||
|
||||
init()
|
||||
{
|
||||
if (!isdefined(level.sunenable))
|
||||
level.sunenable = GetDvarInt( "sm_sunenable", 1 );
|
||||
if (!isdefined(level.sunshadowscale))
|
||||
level.sunshadowscale = GetDvarFloat( "sm_sunshadowscale", 1.0 );
|
||||
if (!isdefined(level.spotlimit))
|
||||
level.spotlimit = GetDvarInt( "sm_spotlimit", 4 );
|
||||
if (!isdefined(level.sunsamplesizenear))
|
||||
level.sunsamplesizenear = GetDvarFloat( "sm_sunsamplesizenear", .25 );
|
||||
if (!isdefined(level.qualityspotshadow))
|
||||
level.qualityspotshadow = GetDvarFloat( "sm_qualityspotshadow", 1.0 );
|
||||
thread monitorPlayerSpawns();
|
||||
|
||||
if ( !IsDefined( level._light ) )
|
||||
{
|
||||
level._light = spawnstruct();
|
||||
light_setup_common_flickerLight_presets();
|
||||
light_message_init();
|
||||
}
|
||||
|
||||
triggers = getentarray( "trigger_multiple_light_sunshadow", "classname" );
|
||||
|
||||
for ( i = 0;i < triggers.size;i ++ )
|
||||
{
|
||||
level thread sun_shadow_trigger( triggers[ i ] );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set_smdvars( sunenable, sunshadowscale, spotlimit, sunsamplesizenear, qualityspotshadow )
|
||||
{
|
||||
if (isdefined(sunenable))
|
||||
level.sunenable = sunenable;
|
||||
if (isdefined(sunshadowscale))
|
||||
level.sunshadowscale = sunshadowscale;
|
||||
if (isdefined(spotlimit))
|
||||
level.spotlimit = spotlimit;
|
||||
if (isdefined(sunsamplesizenear))
|
||||
level.sunsamplesizenear = sunsamplesizenear;
|
||||
if (isdefined(qualityspotshadow))
|
||||
level.qualityspotshadow = qualityspotshadow;
|
||||
}
|
||||
|
||||
monitorPlayerSpawns()
|
||||
{
|
||||
if (isdefined(level.players))
|
||||
{
|
||||
foreach ( player in level.players)
|
||||
{
|
||||
player initPlayer();
|
||||
}
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
level waittill( "connected", player);
|
||||
player initPlayer();
|
||||
player thread monitorDeath();
|
||||
}
|
||||
}
|
||||
|
||||
initPlayer()
|
||||
{
|
||||
self.sunenable = level.sunenable;
|
||||
self.sunshadowscale = level.sunshadowscale;
|
||||
self.spotlimit = level.spotlimit;
|
||||
self.sunsamplesizenear = level.sunsamplesizenear;
|
||||
self.qualityspotshadow = level.qualityspotshadow;
|
||||
self SetClientDvars(
|
||||
"sm_sunenable", self.sunenable,
|
||||
"sm_sunshadowscale", self.sunshadowscale,
|
||||
"sm_spotlimit", self.spotlimit,
|
||||
"sm_qualityspotshadow", self.qualityspotshadow,
|
||||
"sm_sunSampleSizeNear", self.sunsamplesizenear );
|
||||
}
|
||||
|
||||
monitorDeath()
|
||||
{
|
||||
self waittill("spawned");
|
||||
self initPlayer();
|
||||
}
|
||||
|
||||
sun_shadow_trigger( trigger )
|
||||
{
|
||||
duration = 1;
|
||||
if ( IsDefined( trigger.script_duration ) )
|
||||
duration = trigger.script_duration;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
trigger waittill( "trigger", player );
|
||||
// not threading so it waits until it's done before resetting
|
||||
// so it doesn't keep triggering when player stays in place
|
||||
// -Carlos
|
||||
trigger set_sun_shadow_params( duration, player );
|
||||
}
|
||||
}
|
||||
|
||||
set_sun_shadow_params( duration, player )
|
||||
{
|
||||
//Set the following dvars, may add blending later
|
||||
//sm_sunenable = 1
|
||||
//sm_sunshadowscale = 1
|
||||
//sm_spotlimit = 4
|
||||
//sm_sunsamplesizenear = .25
|
||||
sunenable = player.sunenable;
|
||||
sunshadowscale = player.sunshadowscale;
|
||||
spotlimit = player.spotlimit;
|
||||
sunsamplesizenear = player.sunsamplesizenear;
|
||||
qualityspotshadow = player.qualityspotshadow;
|
||||
if ( IsDefined( self.script_sunenable ) ) sunenable = self.script_sunenable;
|
||||
if ( IsDefined( self.script_sunshadowscale ) ) sunshadowscale = self.script_sunshadowscale;
|
||||
if ( IsDefined( self.script_spotlimit ) ) spotlimit = self.script_spotlimit;
|
||||
if ( IsDefined( self.script_sunsamplesizenear ) ) sunsamplesizenear = self.script_sunsamplesizenear;
|
||||
sunsamplesizenear = min( max( 0.016, sunsamplesizenear ), 32 );
|
||||
if ( IsDefined( self.script_qualityspotshadow ) ) qualityspotshadow = self.script_qualityspotshadow;
|
||||
|
||||
player SetClientDvars(
|
||||
"sm_sunenable", sunenable,
|
||||
"sm_sunshadowscale", sunshadowscale,
|
||||
"sm_spotlimit", spotlimit,
|
||||
"sm_qualityspotshadow", qualityspotshadow );
|
||||
player.sunenable = sunenable;
|
||||
player.sunshadowscale = sunshadowscale;
|
||||
player.spotlimit = spotlimit;
|
||||
old_sunsamplesizenear = player.sunsamplesizenear;
|
||||
player.sunsamplesizenear = sunsamplesizenear;
|
||||
player.qualityspotshadow = qualityspotshadow;
|
||||
|
||||
self thread lerp_sunsamplesizenear_overtime( sunsamplesizenear, old_sunsamplesizenear, duration, player );
|
||||
}
|
||||
|
||||
lerp_sunsamplesizenear_overtime( value, old_value, time, player )
|
||||
{
|
||||
level notify( "changing_sunsamplesizenear"+player.name );
|
||||
level endon( "changing_sunsamplesizenear"+player.name );
|
||||
|
||||
if ( value == old_value )
|
||||
return;
|
||||
|
||||
diff = value - old_value;
|
||||
dt = 0.1;
|
||||
|
||||
times = time/dt;
|
||||
|
||||
if ( times > 0 )
|
||||
{
|
||||
d = diff / times;
|
||||
|
||||
v = old_value;
|
||||
for ( i=0; i<times; i++ )
|
||||
{
|
||||
v = v + d;
|
||||
player SetClientDvar( "sm_sunSampleSizeNear", v );
|
||||
player.sunsamplesizenear = v;
|
||||
wait( dt );
|
||||
}
|
||||
}
|
||||
player SetClientDvar( "sm_sunSampleSizeNear", value );
|
||||
player.sunsamplesizenear = value;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// FLICKERING LIGHT Code
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
light_setup_common_flickerLight_presets()
|
||||
{
|
||||
//name color0 color1 minDelay maxDelay intensity
|
||||
create_flickerLight_preset("fire", ( 0.972549, 0.624510, 0.345098 ), ( .2, 0.1462746, 0.0878432 ), .005, .2, 8 );
|
||||
create_flickerLight_preset("blue_fire", ( 0.445098, 0.624510, 0.972549 ), ( .05, 0.1504510, 0.3078432 ), .005, .2, 8 );
|
||||
create_flickerLight_preset("white_fire", ( 0.972549, 0.972549, 0.972549 ), ( .2, 0.2, 0.2 ), .005, .2, 8 );
|
||||
create_flickerLight_preset("pulse", ( 0, 0, 0 ), ( 255, 107, 107 ), .2, 1, 8 );
|
||||
create_flickerLight_preset("lightbulb", ( 0.972549, 0.624510, 0.345098 ), ( .2, 0.1462746, 0.0878432 ), .005, .2, 6 );
|
||||
create_flickerLight_preset("fluorescent", ( 0.972549, 0.624510, 0.345098 ), ( .2, 0.1462746, 0.0878432 ), .005, .2, 7 );
|
||||
create_flickerLight_preset("static_screen", ( 0.63, 0.72, 0.92 ), ( .40, 0.43, 0.48 ), .005, .2, 7 );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: create_flickerLight_preset( <name> , <color0> , <color1> , <minDelay> , <maxDelay> , <intensity> )"
|
||||
"Summary: "
|
||||
"Module: Entity"
|
||||
"CallOn: An entity"
|
||||
"MandatoryArg: <name>: "
|
||||
"MandatoryArg: <color0>: "
|
||||
"MandatoryArg: <color1>: "
|
||||
"MandatoryArg: <minDelay>: "
|
||||
"MandatoryArg: <maxDelay>: "
|
||||
"MandatoryArg: <intensity>: "
|
||||
"Example: "
|
||||
"SPMP: singleplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
create_flickerLight_preset(name, color0, color1, minDelay, maxDelay, intensity)
|
||||
{
|
||||
if (!IsDefined(level._light.flicker_presets))
|
||||
{
|
||||
level._light.flicker_presets = [];
|
||||
}
|
||||
|
||||
new_preset = spawnstruct();
|
||||
new_preset.color0 = color0;
|
||||
new_preset.color1 = color1;
|
||||
new_preset.minDelay = minDelay;
|
||||
new_preset.maxDelay = maxDelay;
|
||||
new_preset.intensity = intensity;
|
||||
|
||||
level._light.flicker_presets[name] = new_preset;
|
||||
|
||||
// dyn_flickerLight(name, color0, color1, minDelay, maxDelay);
|
||||
}
|
||||
|
||||
get_flickerLight_preset(name)
|
||||
{
|
||||
if (IsDefined(level._light.flicker_presets) && IsDefined(level._light.flicker_presets[name]))
|
||||
{
|
||||
return level._light.flicker_presets[name];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: play_flickerLight_preset( <name> , <targetName>, <intensity_> )"
|
||||
"Summary: Plays a flickering light preset (defined with create_flickerLight_preset)"
|
||||
"Module: Lighting"
|
||||
"CallOn: Nothing"
|
||||
"MandatoryArg: <name>: the name of the preset"
|
||||
"MandatoryArg: <targetName>: the targetname of the entity type"
|
||||
"OptionalArg: <intensity_>: optionally override the intensity parameter of the preset"
|
||||
"Example: ent play_flickerLight_preset("fire", "fire_flicker", 4); or play_flickerLight_preset("fire", "fire_flicker");"
|
||||
"SPMP: singleplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
play_flickerLight_preset(name, targetName, intensity_)
|
||||
{
|
||||
assert(IsString(name));
|
||||
assert(IsString(targetName));
|
||||
|
||||
ent = GetEnt( targetName, "targetname" );
|
||||
if( !IsDefined( ent ) )
|
||||
{
|
||||
println("Error Light Scripts: play_flickerLight_preset with name, \"" + name + "\", was called on a non-existant targetName, \"" + targetName + "\".");
|
||||
return;
|
||||
}
|
||||
|
||||
preset = get_flickerLight_preset(name);
|
||||
if (!IsDefined(preset))
|
||||
{
|
||||
PrintLn("Error Light Scripts: flickerLight preset " + name + " is not defined. Please define before calling it with play_flickerLight_preset.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDefined(intensity_))
|
||||
{
|
||||
if (intensity_ < 0)
|
||||
{
|
||||
PrintLn("Warning: flickerLight preset " + name + " is playing with an intensity override less than zero. Truncating.");
|
||||
intensity_ = 0;
|
||||
}
|
||||
|
||||
preset.intensity = intensity_;
|
||||
}
|
||||
|
||||
ent SetLightIntensity( preset.intensity );
|
||||
|
||||
ent.isLightFlickering = true;
|
||||
ent.isLightFlickerPaused = false;
|
||||
|
||||
ent thread dyn_flickerLight( preset.color0, preset.color1, preset.minDelay, preset.MaxDelay );
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: stop_flickerLight( <name> , <targetName> )"
|
||||
"Summary: "
|
||||
"Module: Entity"
|
||||
"CallOn: An entity"
|
||||
"MandatoryArg: <name>: "
|
||||
"MandatoryArg: <targetName>: "
|
||||
"Example: "
|
||||
"SPMP: singleplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
stop_flickerLight(name, targetName, intensity_)
|
||||
{
|
||||
ent = GetEnt( targetName, "targetname" );
|
||||
|
||||
if(!IsDefined(ent))
|
||||
{
|
||||
println("Error Light Scripts: stop_flickerLight, \"" + name + "\", was called on a non-existant targetName, \"" + targetName + "\".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDefined(ent.isLightFlickering))
|
||||
{
|
||||
println("Error Light Scripts: stop_flickerLight was called on a flickering light but flickering light isn't flickering.");
|
||||
return;
|
||||
}
|
||||
if (IsDefined(intensity_))
|
||||
{
|
||||
if (intensity_ < 0)
|
||||
{
|
||||
PrintLn("Warning: flickerLight preset " + name + " is playing with an intensity override less than zero. Truncating.");
|
||||
intensity_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ent SetLightIntensity( intensity_ );
|
||||
|
||||
ent notify("kill_flicker");
|
||||
ent.isLightFlickering = undefined;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: pause_flickerLight( <name> , <targetName> )"
|
||||
"Summary: "
|
||||
"Module: Entity"
|
||||
"CallOn: An entity"
|
||||
"MandatoryArg: <name>: "
|
||||
"MandatoryArg: <targetName>: "
|
||||
"Example: "
|
||||
"SPMP: singleplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
pause_flickerLight(name, targetName)
|
||||
{
|
||||
ent = GetEnt( targetName, "targetname" );
|
||||
|
||||
if(!IsDefined(ent))
|
||||
{
|
||||
println("Error Light Scripts: pause_flickerLight, \"" + name + "\", was called on a non-existant targetName, \"" + targetName + "\".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDefined(ent.isLightFlickering))
|
||||
{
|
||||
println("Error Light Scripts: pause_flickerLight was called on a flickering light but flickering light isn't flickering.");
|
||||
return;
|
||||
}
|
||||
|
||||
ent.isLightFlickerPaused = true;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: unpause_flickerLight( <name> , <targetName> )"
|
||||
"Summary: "
|
||||
"Module: Entity"
|
||||
"CallOn: An entity"
|
||||
"MandatoryArg: <name>: "
|
||||
"MandatoryArg: <targetName>: "
|
||||
"Example: "
|
||||
"SPMP: singleplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
unpause_flickerLight(name, targetName)
|
||||
{
|
||||
ent = GetEnt( targetName, "targetname" );
|
||||
|
||||
if(!IsDefined(ent))
|
||||
{
|
||||
println("Error Light Scripts: unpause_flickerLight, \"" + name + "\", was called on a non-existant targetName, \"" + targetName + "\".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDefined(ent.isLightFlickering))
|
||||
{
|
||||
println("Error Light Scripts: pause_flickerLight was called on a flickering light but flickering light isn't flickering.");
|
||||
return;
|
||||
}
|
||||
|
||||
ent.isLightFlickerPaused = false;
|
||||
}
|
||||
|
||||
///the code below is mostly old and might need updating...
|
||||
|
||||
dyn_flickerLight( color0, color1, minDelay, maxDelay )
|
||||
{
|
||||
assert(IsDefined(self.isLightFlickering));
|
||||
assert(IsDefined(self.isLightFlickerPaused));
|
||||
|
||||
self endon("kill_flicker");
|
||||
toColor = color0;
|
||||
delay = 0.0;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if (self.isLightFlickerPaused)
|
||||
{
|
||||
wait 0.05;
|
||||
}
|
||||
else
|
||||
{
|
||||
fromColor = toColor;
|
||||
toColor = color0 + ( color1 - color0 ) * randomfloat( 1.0 );
|
||||
|
||||
if ( minDelay != maxDelay )
|
||||
delay += randomfloatrange( minDelay, maxDelay );
|
||||
else
|
||||
delay += minDelay;
|
||||
|
||||
if(delay==0) delay+=.0000001;//Prevent divide by zero
|
||||
colorDeltaPerTime = ( fromColor - toColor ) * ( 1 / delay );
|
||||
while ( (delay > 0) && !self.isLightFlickerPaused )
|
||||
{
|
||||
self setLightColor( toColor + colorDeltaPerTime * delay );
|
||||
wait 0.05;
|
||||
delay -= 0.05;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////
|
||||
// FLICKERING MODEL
|
||||
///////////////////////////////////////////////////////
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: model_flicker_preset( <script_noteworthy> , <number_of_flickers> , <fxid1> , <fxid2> )"
|
||||
"Summary: "
|
||||
"Module: Entity"
|
||||
"CallOn: An entity"
|
||||
"MandatoryArg: <script_noteworthy>: "
|
||||
"MandatoryArg: <number_of_flickers>: "
|
||||
"MandatoryArg: <fxid1>: "
|
||||
"MandatoryArg: <fxid2>: "
|
||||
"Example: thread model_flicker_preset("light_flicker_test", 7, 1667, 1668);"
|
||||
"SPMP: singleplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
model_flicker_preset(script_noteworthy, number_of_flickers, fxid1, fxid2 )
|
||||
{
|
||||
assert(IsString(script_noteworthy));
|
||||
|
||||
ent = GetEntArray( script_noteworthy, "script_noteworthy" );
|
||||
if( !IsDefined( ent ) )
|
||||
{
|
||||
println("please define a targetname");
|
||||
return;
|
||||
}
|
||||
|
||||
self endon( "death" );
|
||||
flicker_count = 0;
|
||||
time_between_flicker = RandomFloatRange( .1, .25 );
|
||||
|
||||
if( IsDefined( fxid1 ) )
|
||||
{
|
||||
exploder(fxid1);
|
||||
}
|
||||
|
||||
while (flicker_count < number_of_flickers)
|
||||
{
|
||||
if( IsDefined( fxid2 ) )
|
||||
{
|
||||
exploder(fxid2);
|
||||
}
|
||||
foreach (light in ent)
|
||||
{
|
||||
light Show();
|
||||
}
|
||||
wait time_between_flicker;
|
||||
if( IsDefined( fxid2 ) )
|
||||
{
|
||||
stop_exploder(fxid2);
|
||||
}
|
||||
foreach (light in ent)
|
||||
{
|
||||
light Hide();
|
||||
}
|
||||
flicker_count++;
|
||||
wait time_between_flicker;
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////
|
||||
// Light Message Script Code
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
light_message_init()
|
||||
{
|
||||
assert(IsDefined(level._light));
|
||||
level._light.messages = [];
|
||||
}
|
||||
|
||||
light_debug_dvar_init()
|
||||
{
|
||||
/#
|
||||
SetDvarIfUninitialized( "light_debug_messages", 0 );
|
||||
#/
|
||||
}
|
||||
|
||||
light_register_message( message, callback )
|
||||
{
|
||||
assertEx( IsDefined( level._light ), "Need to call light_message_init() before calling this function." );
|
||||
level._light.messages[message] = callback;
|
||||
}
|
||||
|
||||
light_message( message, arg1, arg2, arg3 )
|
||||
{
|
||||
AssertEx( IsDefined( level._light ), "Need to call light_message_init() before calling this function." );
|
||||
|
||||
if ( IsDefined( level._light.messages[message] ) )
|
||||
{
|
||||
if ( IsDefined( arg3 ) )
|
||||
thread [[ level._light.messages[message] ]]( arg1, arg2, arg3 );
|
||||
else if ( IsDefined( arg2 ) )
|
||||
thread [[ level._light.messages[message] ]]( arg1, arg2 );
|
||||
else if ( IsDefined( arg1 ) )
|
||||
thread [[ level._light.messages[message] ]]( arg1 );
|
||||
else
|
||||
thread [[ level._light.messages[message] ]]();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: stop_exploder( <num> )"
|
||||
"Summary: "
|
||||
"Module: Entity"
|
||||
"CallOn: An entity"
|
||||
"MandatoryArg: <param1>: "
|
||||
"OptionalArg: <param2>: "
|
||||
"Example: "
|
||||
"SPMP: singleplayer"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
stop_exploder( num )
|
||||
{
|
||||
num += "";
|
||||
|
||||
if (isdefined(level.createFXexploders))
|
||||
{ // do optimized flavor if available
|
||||
exploders = level.createFXexploders[num];
|
||||
if (isdefined(exploders))
|
||||
{
|
||||
foreach (ent in exploders)
|
||||
{
|
||||
if ( !isdefined( ent.looper ) )
|
||||
continue;
|
||||
|
||||
ent.looper Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( i = 0; i < level.createFXent.size; i++ )
|
||||
{
|
||||
ent = level.createFXent[ i ];
|
||||
if ( !isdefined( ent ) )
|
||||
continue;
|
||||
|
||||
if ( ent.v[ "type" ] != "exploder" )
|
||||
continue;
|
||||
|
||||
// make the exploder actually removed the array instead?
|
||||
if ( !isdefined( ent.v[ "exploder" ] ) )
|
||||
continue;
|
||||
|
||||
if ( ent.v[ "exploder" ] + "" != num )
|
||||
continue;
|
||||
|
||||
if ( !isdefined( ent.looper ) )
|
||||
continue;
|
||||
|
||||
ent.looper Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: exploder( <num> )"
|
||||
"Summary: Sets off the desired exploder"
|
||||
"Module: Utility"
|
||||
"MandatoryArg: <num>: The exploder number"
|
||||
"Example: exploder( 5 );"
|
||||
"SPMP: both"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
exploder( num )
|
||||
{
|
||||
[[ level.exploderFunction ]]( num );
|
||||
}
|
||||
|
||||
|
157
raw/maps/mp/_mutebomb.gsc
Normal file
157
raw/maps/mp/_mutebomb.gsc
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
Adrenaline Boost
|
||||
Author: Ben Retan
|
||||
Description: A piece of equipment used to give you temporarily increased speed
|
||||
*/
|
||||
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\perks\_perkfunctions;
|
||||
|
||||
|
||||
CONST_mute_device_radius_min = 350; // Radius at which sounds are fully attenuated
|
||||
CONST_mute_device_radius_max = 600; // Radius at which sounds are no longer attenuated
|
||||
CONST_mute_device_timeout = 20; // How long the muting effect should last
|
||||
CONST_mute_device_fade_time = 0.25; // How long it should take to fade out the muting effect when the device expires
|
||||
|
||||
watchMuteBombUsage()
|
||||
{
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
if(!IsDefined(level.adrenalineSettings))
|
||||
{
|
||||
MuteBombInit();
|
||||
}
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "grenade_fire", mute_device, weapname );
|
||||
if ( weapname == "mute_bomb_mp" )
|
||||
{
|
||||
if( !IsAlive( self ) )
|
||||
{
|
||||
mute_device delete();
|
||||
return;
|
||||
}
|
||||
|
||||
thread tryUseMuteBomb( mute_device );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MuteBombInit()
|
||||
{
|
||||
self.adrenalineSettings = SpawnStruct();
|
||||
|
||||
}
|
||||
|
||||
tryUseMuteBomb( mute_device ) // self == player
|
||||
{
|
||||
// Force init (this is for test bots)
|
||||
if(!IsDefined(self.adrenalineSettings))
|
||||
{
|
||||
MuteBombInit();
|
||||
}
|
||||
|
||||
thread StartMuteBomb( mute_device );
|
||||
thread MonitorPlayerDeath( mute_device );
|
||||
mute_device thread MonitorMuteBombDeath();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StartMuteBomb( mute_device )
|
||||
{
|
||||
self endon("ClearMuteBomb");
|
||||
self endon("death");
|
||||
mute_device endon( "death" );
|
||||
|
||||
mute_device PlaySound( "mute_device_activate" );
|
||||
wait( 0.75 );
|
||||
|
||||
mute_device AddSoundMuteDevice( CONST_mute_device_radius_min, CONST_mute_device_radius_max, CONST_mute_device_fade_time );
|
||||
mute_device PlayLoopSound( "mute_device_active_lp" );
|
||||
mute_device thread MonitorMuteBombPlayers();
|
||||
|
||||
wait( CONST_mute_device_timeout );
|
||||
|
||||
mute_device ScaleVolume( 0.0, CONST_mute_device_fade_time );
|
||||
mute_device RemoveSoundMuteDevice( CONST_mute_device_fade_time );
|
||||
mute_device notify( "ShutdownMuteBomb" );
|
||||
|
||||
wait( CONST_mute_device_fade_time );
|
||||
|
||||
mute_device StopLoopSound();
|
||||
}
|
||||
|
||||
|
||||
MonitorPlayerDeath( mute_device )
|
||||
{
|
||||
mute_device endon( "ShutdownMuteBomb" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
if ( IsDefined( mute_device ) )
|
||||
{
|
||||
mute_device RemoveSoundMuteDevice( CONST_mute_device_fade_time );
|
||||
mute_device notify( "ShutdownMuteBomb" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MonitorMuteBombDeath() // self == mute_device
|
||||
{
|
||||
self endon( "ShutdownMuteBomb" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
if ( IsDefined( self ) )
|
||||
{
|
||||
self RemoveSoundMuteDevice( CONST_mute_device_fade_time );
|
||||
self notify( "ShutdownMuteBomb" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MonitorMuteBombPlayers() // self == mute_device
|
||||
{
|
||||
self endon( "ShutdownMuteBomb" );
|
||||
|
||||
// Use a radius halfway between the min and max
|
||||
radius = CONST_mute_device_radius_min + ( ( CONST_mute_device_radius_max - CONST_mute_device_radius_min ) / 2 );
|
||||
|
||||
self.touchingPlayers = [];
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
dist = Distance( self.origin, player.origin );
|
||||
touching = array_contains( self.touchingPlayers, player );
|
||||
|
||||
if ( dist <= radius )
|
||||
{
|
||||
if ( !touching )
|
||||
{
|
||||
self.touchingPlayers = array_add( self.touchingPlayers, player );
|
||||
player PlayLocalSound( "mute_device_active_enter" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( touching )
|
||||
{
|
||||
self.touchingPlayers = array_remove( self.touchingPlayers, player );
|
||||
player PlayLocalSound( "mute_device_active_exit" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
276
raw/maps/mp/_na45.gsc
Normal file
276
raw/maps/mp/_na45.gsc
Normal file
@ -0,0 +1,276 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
|
||||
DISTANCE_TO_DETONATE = 64;
|
||||
/#
|
||||
DEBUG = false;
|
||||
#/
|
||||
|
||||
main()
|
||||
{
|
||||
level._effect[ "primer_light" ] = LoadFx( "vfx/lights/light_beacon_m990_spike" );
|
||||
level._effect[ "na45_explosion" ] = LoadFx( "vfx/explosion/frag_grenade_default" );
|
||||
level._effect[ "na45_explosion_body" ] = LoadFx( "vfx/explosion/frag_grenade_default_nodecal" );
|
||||
|
||||
self thread monitor_na45_use();
|
||||
}
|
||||
|
||||
monitor_na45_use()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
self childthread reset_shot_on_reload();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
weapon = self GetCurrentWeapon();
|
||||
if ( IsSubStr( weapon, "m990" ) )
|
||||
{
|
||||
self.bulletHitCallback = ::m990_hit;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.bulletHitCallback = undefined;
|
||||
}
|
||||
|
||||
self waittill( "weapon_change" );
|
||||
}
|
||||
}
|
||||
|
||||
transfer_primer_to_corpse( hit_ent, part_name )
|
||||
{
|
||||
self endon( "primer_deleted" );
|
||||
|
||||
hit_ent waittill( "death" );
|
||||
|
||||
corpse_ent = hit_ent GetCorpseEntity();
|
||||
if ( IsDefined( corpse_ent ) )
|
||||
{
|
||||
if ( IsDefined( part_name ) )
|
||||
{
|
||||
self.primer LinkTo( corpse_ent, part_name );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.primer LinkTo( corpse_ent );
|
||||
}
|
||||
|
||||
// restart fx
|
||||
self.primer thread show_primer_fx( self );
|
||||
}
|
||||
}
|
||||
|
||||
m990_hit( weapon, hit_pos, hit_normal, hit_ent, hit_dir, part_name )
|
||||
{
|
||||
if ( !IsSubStr( weapon, "m990" ) )
|
||||
return;
|
||||
|
||||
if( self GetCurrentWeapon() != weapon )
|
||||
return;
|
||||
|
||||
gun = get_current_shot();
|
||||
|
||||
if ( gun == "primer" )
|
||||
{
|
||||
if ( IsDefined( self.primer ) )
|
||||
{
|
||||
self delete_primer();
|
||||
}
|
||||
|
||||
//place a primer on the target, link the primer if it's a player
|
||||
self.primer = spawn_tag_origin();
|
||||
|
||||
if( IsDefined( hit_ent ) && ( IsPlayer( hit_ent ) || IsAgent( hit_ent ) ) )
|
||||
{
|
||||
self.primer.origin = hit_pos + hit_dir;
|
||||
self.primer.angles = VectorToAngles( hit_dir * -1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.primer.origin = hit_pos;
|
||||
self.primer.angles = VectorToAngles( hit_normal );
|
||||
}
|
||||
|
||||
self.primer Show();
|
||||
|
||||
self.primer thread show_primer_fx( self );
|
||||
|
||||
if ( IsDefined( hit_ent ) )
|
||||
{
|
||||
primer_link_ent = hit_ent;
|
||||
if ( IsPlayer( hit_ent ) || IsAgent( hit_ent ) )
|
||||
{
|
||||
if ( IsAlive( hit_ent ) )
|
||||
{
|
||||
self thread transfer_primer_to_corpse( hit_ent, part_name );
|
||||
}
|
||||
else
|
||||
{
|
||||
corpse_ent = hit_ent GetCorpseEntity();
|
||||
if ( IsDefined( corpse_ent ) )
|
||||
{
|
||||
primer_link_ent = corpse_ent;
|
||||
}
|
||||
}
|
||||
self.primer.onBody = true;
|
||||
}
|
||||
|
||||
if ( IsDefined( part_name ) )
|
||||
{
|
||||
self.primer LinkTo( primer_link_ent, part_name );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.primer LinkTo( primer_link_ent );
|
||||
}
|
||||
}
|
||||
|
||||
self thread cleanup_primer();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( self.primer ) )
|
||||
{
|
||||
/#
|
||||
if( IsDefined( DEBUG ) && DEBUG )
|
||||
{
|
||||
IPrintLnBold( Distance( hit_pos, self.primer.origin ) );
|
||||
level thread draw_distance_line( hit_pos, self.primer.origin, 5 );
|
||||
}
|
||||
#/
|
||||
|
||||
//if we're within a certain distance of the primer, explode
|
||||
if( Distance( hit_pos, self.primer.origin ) <= DISTANCE_TO_DETONATE )
|
||||
{
|
||||
if( isDefined( self.primer.onBody ) )
|
||||
PlayFX( getfx( "na45_explosion_body" ), self.primer.origin, AnglesToForward( self.primer.angles ) );
|
||||
else
|
||||
PlayFX( getfx( "na45_explosion" ), self.primer.origin, AnglesToForward( self.primer.angles ) );
|
||||
playSoundAtPos( self.origin, "wpn_na45_exp");
|
||||
|
||||
//account for loot variations to adjust explosion damage
|
||||
weapon = self GetCurrentWeapon();
|
||||
|
||||
radius = 256;
|
||||
max_damage = 130;
|
||||
min_damage = 15;
|
||||
|
||||
if( IsSubStr( weapon, "m990loot2" ) )
|
||||
{
|
||||
min_damage = 30;
|
||||
}
|
||||
|
||||
if( IsSubStr( weapon, "m990loot5" ) )
|
||||
{
|
||||
max_damage = 100;
|
||||
radius = 196;
|
||||
}
|
||||
|
||||
if( IsSubStr( weapon, "m990loot8" ) )
|
||||
{
|
||||
max_damage = 100;
|
||||
}
|
||||
|
||||
if( IsSubStr( weapon, "m990loot9" ) )
|
||||
{
|
||||
min_damage = 30;
|
||||
max_damage = 150;
|
||||
radius = 300;
|
||||
}
|
||||
|
||||
//numbers are the same as frag_grenade_mp
|
||||
PhysicsExplosionSphere( self.primer.origin, 256, 32, 1.0 );
|
||||
RadiusDamage( self.primer.origin, radius, max_damage, min_damage, self, "MOD_EXPLOSIVE", weapon );
|
||||
}
|
||||
|
||||
self delete_primer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset_shot_on_reload()
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
self waittill( "reload_start" );
|
||||
|
||||
weapon = self GetCurrentWeapon();
|
||||
if( !IsSubStr( weapon, "m990" ) )
|
||||
{
|
||||
self waittill( "weapon_change" );
|
||||
continue;
|
||||
}
|
||||
|
||||
self cleanup_primer_reload();
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_primer()
|
||||
{
|
||||
self endon( "primer_deleted" );
|
||||
|
||||
self waittill_any( "death", "disconnect", "faux_spawn" );
|
||||
|
||||
if ( IsDefined( self ) && IsDefined( self.primer ) )
|
||||
{
|
||||
self thread delete_primer();
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_primer_reload()
|
||||
{
|
||||
self endon( "primer_deleted" );
|
||||
|
||||
if ( IsDefined( self ) && IsDefined( self.primer ) )
|
||||
{
|
||||
self thread delete_primer();
|
||||
}
|
||||
}
|
||||
|
||||
get_current_shot()
|
||||
{
|
||||
ammo_left = self GetCurrentWeaponClipAmmo();
|
||||
if ( ammo_left % 2 == 1 )
|
||||
{
|
||||
return "primer";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "catalyst";
|
||||
}
|
||||
}
|
||||
|
||||
show_primer_fx( player )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
wait 0.1;
|
||||
PlayFXOnTagForClients( getfx( "primer_light" ), self, "TAG_ORIGIN", player );
|
||||
}
|
||||
|
||||
delete_primer()
|
||||
{
|
||||
self notify( "primer_deleted" );
|
||||
|
||||
StopFXOnTag( getfx( "primer_light" ), self.primer, "TAG_ORIGIN" );
|
||||
self.primer Delete();
|
||||
self.primer = undefined;
|
||||
}
|
||||
|
||||
draw_distance_line( org1, org2, timer, color )
|
||||
{
|
||||
if( !IsDefined( color ) )
|
||||
color = ( 1, 0, 0 );
|
||||
|
||||
while( timer > 0 )
|
||||
{
|
||||
Line( org1, org2, color, 1, false, 1 );
|
||||
timer -= 0.05;
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
69
raw/maps/mp/_opticsthermal.gsc
Normal file
69
raw/maps/mp/_opticsthermal.gsc
Normal file
@ -0,0 +1,69 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
opticsthermal_think()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
if ( IsAgent ( self ) )
|
||||
return;
|
||||
|
||||
ads_frac = 0.65;
|
||||
|
||||
self.opticsThermalEnabled = false;
|
||||
self.orbitalThermalMode = false;
|
||||
|
||||
while( true )
|
||||
{
|
||||
has_opticsthermal = false;
|
||||
|
||||
attachment_list = GetWeaponAttachments( self GetCurrentWeapon() );
|
||||
|
||||
if( isDefined( attachment_list ) )
|
||||
{
|
||||
foreach( attachment in attachment_list )
|
||||
{
|
||||
if( isSubStr( attachment, "opticsthermal" ) )
|
||||
{
|
||||
has_opticsthermal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Condition to turn off optics thermal
|
||||
disable_opticsthermal = !has_opticsthermal;
|
||||
disable_opticsthermal |= ( has_opticsthermal && ( self PlayerAds() < ads_frac ) );
|
||||
disable_opticsthermal |= ( self IsUsingTurret() );
|
||||
disable_opticsthermal |= ( self.orbitalThermalMode );
|
||||
|
||||
if ( disable_opticsthermal )
|
||||
opticsthermal_blur_off( self );
|
||||
else
|
||||
opticsthermal_blur( self, 0.05 );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
opticsthermal_blur( player, duration )
|
||||
{
|
||||
if ( player.opticsThermalEnabled )
|
||||
return;
|
||||
|
||||
player EnablePhysicalDepthOfFieldScripting(3);
|
||||
player SetPhysicalDepthOfField( 70, 0, 40, 80 );
|
||||
player.opticsThermalEnabled = true;
|
||||
}
|
||||
|
||||
opticsthermal_blur_off( player )
|
||||
{
|
||||
if ( !player.opticsThermalEnabled )
|
||||
return;
|
||||
|
||||
player DisablePhysicalDepthOfFieldScripting();
|
||||
player.opticsThermalEnabled = false;
|
||||
}
|
264
raw/maps/mp/_reinforcements.gsc
Normal file
264
raw/maps/mp/_reinforcements.gsc
Normal file
@ -0,0 +1,264 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
REINFORCEMENT_TYPE_NONE = 0;
|
||||
REINFORCEMENT_TYPE_COMMON = 1;
|
||||
REINFORCEMENT_TYPE_UNCOMMON = 2;
|
||||
REINFORCEMENT_TYPE_RARE = 3;
|
||||
REINFORCEMENT_TYPE_PRACTICE = 4;
|
||||
|
||||
init()
|
||||
{
|
||||
/#
|
||||
SetDvarIfUninitialized( "scr_force_reinforcements", 0 );
|
||||
|
||||
if( GetDvarInt( "scr_isGamescom" ) == 1 )
|
||||
SetDvarIfUninitialized( "scr_force_reinforcements_randomly", 0 );
|
||||
else
|
||||
SetDvarIfUninitialized( "scr_force_reinforcements_randomly", 1 );
|
||||
|
||||
#/
|
||||
|
||||
if ( level.rankedMatch )
|
||||
level thread onPlayerConnect();
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
level waittill ( "prematch_over" );
|
||||
waitframe();
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( IsBot( player ) )
|
||||
continue;
|
||||
|
||||
player resetReinforcements();
|
||||
player thread checkForReinforcements();
|
||||
}
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
if ( IsBot( player ) )
|
||||
continue;
|
||||
|
||||
player resetReinforcements();
|
||||
player thread checkForReinforcements();
|
||||
}
|
||||
}
|
||||
|
||||
resetReinforcements() // self == player
|
||||
{
|
||||
self SetClientOmnvar( "ui_reinforcement_timer_type", 0 );
|
||||
self SetClientOmnvar( "ui_reinforcement_timer", 0 );
|
||||
}
|
||||
|
||||
storePendingReinforcement( type ) // self == player
|
||||
{
|
||||
self.pers["reinforcements"] = SpawnStruct();
|
||||
self.pers["reinforcements"].type = type;
|
||||
self.pers["reinforcements"].startTimePassed = getGameTimePassedMS();
|
||||
}
|
||||
|
||||
cancelPendingReinforcement() // self == player
|
||||
{
|
||||
self.pers["reinforcements"].type = 0;
|
||||
}
|
||||
|
||||
pendingReinforcementAvailable() // self == player
|
||||
{
|
||||
return IsDefined( self.pers["reinforcements"] );
|
||||
}
|
||||
|
||||
checkForReinforcements() // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
if( !allowClassChoice() )
|
||||
return;
|
||||
|
||||
// Wait until the player has joined a team
|
||||
if ( self.health <= 0 )
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
// Handle round-switching
|
||||
if ( self pendingReinforcementAvailable() )
|
||||
{
|
||||
self thread continueReinforcements();
|
||||
return;
|
||||
}
|
||||
|
||||
typeAvailable = REINFORCEMENT_TYPE_NONE;
|
||||
|
||||
if ( practiceRoundGame() )
|
||||
{
|
||||
typeAvailable = REINFORCEMENT_TYPE_PRACTICE;
|
||||
}
|
||||
else
|
||||
{
|
||||
typeAvailable = self ConsumeReinforcement();
|
||||
|
||||
|
||||
/#
|
||||
//giving every player a chance of a reinforcement on connect. ONLY FOR TESTING.
|
||||
if ( GetDvarInt( "scr_force_reinforcements_randomly", 0 ) > 0 )
|
||||
{
|
||||
i = RandomInt( 100 );
|
||||
|
||||
if ( i < 75 )
|
||||
typeAvailable = 0;
|
||||
else if ( i < 90 )
|
||||
typeAvailable = 1;
|
||||
else if ( i < 98 )
|
||||
typeAvailable = 2;
|
||||
else if ( i < 100 )
|
||||
typeAvailable = 3;
|
||||
|
||||
}
|
||||
|
||||
if ( GetDvarInt( "scr_force_reinforcements", 0 ) > 0 )
|
||||
typeAvailable = GetDvarInt( "scr_force_reinforcements", 0 );
|
||||
|
||||
SetDvar( "scr_force_reinforcements_randomly", 0 );
|
||||
SetDvar( "src_force_reinforcements", 0 );
|
||||
#/
|
||||
}
|
||||
|
||||
// Record data about reinforcements available
|
||||
storePendingReinforcement( typeAvailable );
|
||||
|
||||
if ( typeAvailable == REINFORCEMENT_TYPE_NONE )
|
||||
return;
|
||||
|
||||
timeToReinforcementMS = getTimeToReinforcementForTypeMS( typeAvailable );
|
||||
matchTimeRemainingMS = getTimeRemainingIncludingRounds();
|
||||
|
||||
if ( matchTimeRemainingMS < timeToReinforcementMS )
|
||||
{
|
||||
// Cancel as there isn't enough time left in the match
|
||||
cancelPendingReinforcement();
|
||||
return;
|
||||
}
|
||||
|
||||
self thread waitForReinforcementOfType( typeAvailable, timeToReinforcementMS );
|
||||
}
|
||||
|
||||
continueReinforcements() // self == player
|
||||
{
|
||||
typeAvailable = self.pers["reinforcements"].type;
|
||||
if ( typeAvailable == REINFORCEMENT_TYPE_NONE )
|
||||
return;
|
||||
|
||||
timeToReinforcementMS = getTimeToReinforcementForTypeMS( typeAvailable );
|
||||
matchTimeElapsedMS = getGameTimePassedMS();
|
||||
startTimeElapsedMS = self.pers["reinforcements"].startTimePassed;
|
||||
|
||||
timeToReinforcementMS -= matchTimeElapsedMS;
|
||||
timeToReinforcementMS += startTimeElapsedMS;
|
||||
|
||||
self thread waitForReinforcementOfType( typeAvailable, timeToReinforcementMS );
|
||||
}
|
||||
|
||||
getTimeRemainingIncludingRounds()
|
||||
{
|
||||
if ( isRoundBased() )
|
||||
{
|
||||
numRoundsToWin = getScoreLimit();
|
||||
minRoundsRemaining = min( numRoundsToWin - getRoundsWon( "allies" ), numRoundsToWin - getRoundsWon( "axis" ) );
|
||||
timeLimitMin = getTimeLimit() * minRoundsRemaining;
|
||||
return ( timeLimitMin * 60 * 1000 ) - getTimePassed();
|
||||
}
|
||||
else
|
||||
{
|
||||
return maps\mp\gametypes\_gamelogic::getTimeRemaining();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getTimeToReinforcementForTypeMS( type ) // self == player
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case REINFORCEMENT_TYPE_COMMON: return 2 * 60 * 1000; // 2 minutes
|
||||
case REINFORCEMENT_TYPE_UNCOMMON: return 4 * 60 * 1000; // 4 minutes
|
||||
case REINFORCEMENT_TYPE_RARE: return 6 * 60 * 1000; // 6 minutes
|
||||
case REINFORCEMENT_TYPE_PRACTICE: return 1 * 60 * 1000; // 1 minute
|
||||
default:
|
||||
AssertMsg( "getTimeToReinforcementForType(): Unhandled reinforcement type: " + type );
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getIconTypeForReinforcementOfType( type ) // self == player
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case REINFORCEMENT_TYPE_COMMON: return 1; // Common
|
||||
case REINFORCEMENT_TYPE_UNCOMMON: return 2; // Uncommon
|
||||
case REINFORCEMENT_TYPE_RARE: return 3; // Rare
|
||||
case REINFORCEMENT_TYPE_PRACTICE: return 1; // Common
|
||||
default:
|
||||
AssertMsg( "getIconTypeForReinforcementOfType(): Unhandled reinforcement type: " + type );
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getCarePackageStreakForReinforcementOfType( type ) // self == player
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case REINFORCEMENT_TYPE_COMMON: return "airdrop_reinforcement_common";
|
||||
case REINFORCEMENT_TYPE_UNCOMMON: return "airdrop_reinforcement_uncommon";
|
||||
case REINFORCEMENT_TYPE_RARE: return "airdrop_reinforcement_rare";
|
||||
case REINFORCEMENT_TYPE_PRACTICE: return "airdrop_reinforcement_practice";
|
||||
default:
|
||||
AssertMsg( "getCarePackageStreakForReinforcementOfType(): Unhandled reinforcement type: " + type );
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
waitForReinforcementOfType( type, timeMS ) // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
Assert( type != REINFORCEMENT_TYPE_NONE );
|
||||
|
||||
iconType = getIconTypeForReinforcementOfType( type );
|
||||
|
||||
self SetClientOmnvar( "ui_reinforcement_timer_type", iconType );
|
||||
self SetClientOmnvar( "ui_reinforcement_timer", ( GetTime() + timeMS ) );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeMS / 1000.0 );
|
||||
|
||||
self SetClientOmnvar( "ui_reinforcement_timer_type", 0 );
|
||||
self SetClientOmnvar( "ui_reinforcement_timer", 0 );
|
||||
|
||||
// Give the package of type
|
||||
giveReinforcementOfType( type );
|
||||
}
|
||||
|
||||
giveReinforcementOfType( type ) // self == player
|
||||
{
|
||||
if ( !IsPlayer( self ) )
|
||||
return;
|
||||
|
||||
streakName = getCarePackageStreakForReinforcementOfType( type );
|
||||
streakVal = 500;
|
||||
|
||||
slotIndex = self maps\mp\killstreaks\_killstreaks::getNextKillstreakSlotIndex( streakName );
|
||||
self thread maps\mp\gametypes\_hud_message::killstreakSplashNotify( streakName, streakVal, undefined, undefined, slotIndex );
|
||||
self maps\mp\killstreaks\_killstreaks::giveKillstreak( streakName );
|
||||
|
||||
cancelPendingReinforcement();
|
||||
}
|
814
raw/maps/mp/_riotshield.gsc
Normal file
814
raw/maps/mp/_riotshield.gsc
Normal file
@ -0,0 +1,814 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.riot_shield_names = [];
|
||||
level.riot_shield_names[level.riot_shield_names.size] = "riotshield_mp";
|
||||
level.riot_shield_names[level.riot_shield_names.size] = "iw5_riotshieldt6_mp";
|
||||
level.riot_shield_names[level.riot_shield_names.size] = "iw5_riotshieldt6loot0_mp";
|
||||
level.riot_shield_names[level.riot_shield_names.size] = "iw5_riotshieldt6loot1_mp";
|
||||
level.riot_shield_names[level.riot_shield_names.size] = "iw5_riotshieldt6loot2_mp";
|
||||
level.riot_shield_names[level.riot_shield_names.size] = "iw5_riotshieldt6loot3_mp";
|
||||
level.riot_shield_names[level.riot_shield_names.size] = "iw5_riotshieldjugg_mp";
|
||||
|
||||
precacheAnims();
|
||||
|
||||
// loadfx( "weapon/riotshield/fx_riotshield_depoly_lights" );
|
||||
// loadfx( "weapon/riotshield/fx_riotshield_depoly_dust" );
|
||||
|
||||
level.riot_shield_collision = GetEnt( "riot_shield_collision", "targetname" );
|
||||
|
||||
level._effect[ "riot_shield_shock_fx" ] = LoadFX( "vfx/explosion/riotshield_stun" );
|
||||
level._effect[ "riot_shield_deploy_smoke" ] = LoadFX( "vfx/smoke/riotshield_deploy_smoke" );
|
||||
level._effect[ "riot_shield_deploy_lights" ] = LoadFX( "vfx/lights/riotshield_deploy_lights" );
|
||||
}
|
||||
|
||||
|
||||
#using_animtree ( "mp_riotshield" );
|
||||
precacheAnims()
|
||||
{
|
||||
PrecacheMpAnim( "npc_deployable_riotshield_stand_deploy" );
|
||||
PrecacheMpAnim( "npc_deployable_riotshield_stand_destroyed" );
|
||||
PrecacheMpAnim( "npc_deployable_riotshield_stand_shot" );
|
||||
PrecacheMpAnim( "npc_deployable_riotshield_stand_shot_back" );
|
||||
PrecacheMpAnim( "npc_deployable_riotshield_stand_melee_front" );
|
||||
PrecacheMpAnim( "npc_deployable_riotshield_stand_melee_back" );
|
||||
}
|
||||
|
||||
|
||||
hasRiotShield() // self == player
|
||||
{
|
||||
return ( IsDefined( self.frontShieldModel ) || IsDefined( self.backShieldModel ) );
|
||||
}
|
||||
|
||||
|
||||
hasRiotShieldEquipped() // self == player
|
||||
{
|
||||
return ( IsDefined( self.frontShieldModel ) );
|
||||
}
|
||||
|
||||
|
||||
weaponIsRiotShield( inWeaponName )
|
||||
{
|
||||
inBaseWeaponName = GetWeaponBaseName( inWeaponName );
|
||||
if ( !IsDefined( inBaseWeaponName ) )
|
||||
inBaseWeaponName = inWeaponName;
|
||||
|
||||
foreach( weaponName in level.riot_shield_names )
|
||||
{
|
||||
if ( weaponName == inBaseWeaponName )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
weaponIsShockPlantRiotShield( inWeaponName )
|
||||
{
|
||||
if ( !weaponIsRiotShield( inWeaponName ) )
|
||||
return false;
|
||||
|
||||
return IsSubStr( inWeaponName, "shockplant" );
|
||||
}
|
||||
|
||||
|
||||
getOtherRiotShieldName( inWeaponName )
|
||||
{
|
||||
foundInputWeapon = false;
|
||||
weapons = self GetWeaponsListPrimaries();
|
||||
foreach( weapon in weapons )
|
||||
{
|
||||
if ( weaponIsRiotShield( weapon ) )
|
||||
{
|
||||
if ( ( weapon == inWeaponName ) && !foundInputWeapon )
|
||||
foundInputWeapon = true;
|
||||
else
|
||||
return weapon;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
updateFrontAndBackShields( newWeapon ) // self == player
|
||||
{
|
||||
// note this function must play nice with _detachAll().
|
||||
self.frontShieldModel = undefined;
|
||||
self.backShieldModel = undefined;
|
||||
|
||||
if ( !IsDefined( newWeapon ) )
|
||||
newWeapon = self GetCurrentPrimaryWeapon();
|
||||
|
||||
if ( weaponIsRiotShield( newWeapon ) )
|
||||
{
|
||||
self.frontShieldModel = GetWeaponModel( newWeapon );
|
||||
}
|
||||
|
||||
otherShield = getOtherRiotShieldName( newWeapon );
|
||||
if ( IsDefined( otherShield ) )
|
||||
{
|
||||
assert( weaponIsRiotShield( otherShield ) );
|
||||
self.backShieldModel = GetWeaponModel( otherShield );
|
||||
}
|
||||
|
||||
self RefreshShieldModels( newWeapon );
|
||||
}
|
||||
|
||||
|
||||
riotShield_clear()
|
||||
{
|
||||
self.frontShieldModel = undefined;
|
||||
self.backShieldModel = undefined;
|
||||
}
|
||||
|
||||
entIsStuckToShield()
|
||||
{
|
||||
if ( !self IsLinked() )
|
||||
return false;
|
||||
|
||||
tagName = self GetLinkedTagName();
|
||||
if ( !IsDefined( tagName ) )
|
||||
return false;
|
||||
|
||||
switch( tagName )
|
||||
{
|
||||
case "tag_weapon_left": // Front shield
|
||||
case "tag_shield_back": // Back shield
|
||||
case "tag_inhand": // Exo shield
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
watchRiotShieldUse() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon ( "faux_spawn" );
|
||||
|
||||
// watcher for attaching the model to correct player bones
|
||||
self thread trackRiotShield();
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill( "raise_riotshield" );
|
||||
self thread startRiotshieldDeploy();
|
||||
}
|
||||
}
|
||||
|
||||
riotshield_watch_for_change_weapon() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "riotshield_change_weapon" );
|
||||
|
||||
newWeapon = undefined;
|
||||
|
||||
self waittill( "weapon_change", newWeapon );
|
||||
|
||||
self notify ( "riotshield_change_weapon", newWeapon );
|
||||
}
|
||||
|
||||
riotshield_watch_for_start_change_weapon() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "riotshield_change_weapon" );
|
||||
|
||||
newWeapon = undefined;
|
||||
|
||||
// SetDvarIfUninitialized( "scr_twoshield_wait", 1.0 );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
self waittill( "weapon_switch_started", newWeapon );
|
||||
|
||||
// Climbing a ladder instantly switches
|
||||
if ( self IsOnLadder() )
|
||||
{
|
||||
self thread riotshield_watch_for_ladder_early_exit();
|
||||
break;
|
||||
}
|
||||
|
||||
// If you have two shields, handle the swap time manually
|
||||
if ( IsDefined( self.frontShieldModel ) && IsDefined( self.backShieldModel ) )
|
||||
{
|
||||
// wait( GetDvarFloat( "scr_twoshield_wait", 1.0 ) );
|
||||
wait( 0.5 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self notify ( "riotshield_change_weapon", newWeapon );
|
||||
}
|
||||
|
||||
riotshield_watch_for_ladder_early_exit() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "weapon_change" ); // Stop worrying once the ladder weapon change completes
|
||||
|
||||
while( self IsOnLadder() )
|
||||
waitframe();
|
||||
|
||||
self notify ( "riotshield_change_weapon", self GetCurrentPrimaryWeapon() );
|
||||
}
|
||||
|
||||
riotshield_watch_for_exo_shield_pullback() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "riotshield_change_weapon" );
|
||||
|
||||
newWeapon = undefined;
|
||||
|
||||
exo_shield_weapon = maps\mp\_exo_shield::get_exo_shield_weapon();
|
||||
|
||||
self waittillmatch( "grenade_pullback", exo_shield_weapon );
|
||||
|
||||
// Allow time for the flag to get set
|
||||
while( !IsDefined( self.exo_shield_on ) || !self.exo_shield_on )
|
||||
waitframe();
|
||||
|
||||
self notify ( "riotshield_change_weapon", exo_shield_weapon );
|
||||
}
|
||||
|
||||
riotshield_watch_for_exo_shield_release() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "riotshield_change_weapon" );
|
||||
|
||||
if ( !IsDefined( self.exo_shield_on ) || !self.exo_shield_on )
|
||||
return;
|
||||
|
||||
newWeapon = undefined;
|
||||
|
||||
exo_shield_weapon = maps\mp\_exo_shield::get_exo_shield_weapon();
|
||||
|
||||
self waittillmatch( "battery_discharge_end", exo_shield_weapon );
|
||||
|
||||
// Allow time for the flag to clear
|
||||
while( IsDefined( self.exo_shield_on ) && self.exo_shield_on )
|
||||
waitframe();
|
||||
|
||||
self notify ( "riotshield_change_weapon", self getCurrentWeapon() );
|
||||
}
|
||||
|
||||
|
||||
trackRiotShield() // self == player
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
self endon ( "faux_spawn" );
|
||||
|
||||
self notify( "track_riot_shield" );
|
||||
self endon ( "track_riot_shield" );
|
||||
|
||||
self updateFrontAndBackShields( self.currentWeaponAtSpawn );
|
||||
|
||||
self.lastNonShieldWeapon = "none";
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self thread watchRiotshieldPickup();
|
||||
|
||||
prevWeapon = self getCurrentWeapon();
|
||||
if ( IsDefined( self.exo_shield_on ) && self.exo_shield_on )
|
||||
prevWeapon = maps\mp\_exo_shield::get_exo_shield_weapon();
|
||||
|
||||
self thread riotshield_watch_for_change_weapon();
|
||||
self thread riotshield_watch_for_start_change_weapon();
|
||||
self thread riotshield_watch_for_exo_shield_pullback();
|
||||
self thread riotshield_watch_for_exo_shield_release();
|
||||
self waittill ( "riotshield_change_weapon", newWeapon );
|
||||
|
||||
if ( weaponIsRiotShield( newWeapon ) )
|
||||
{
|
||||
if ( self hasRiotShield() )
|
||||
{
|
||||
if ( IsDefined( self.riotshieldTakeWeapon ))
|
||||
{
|
||||
self TakeWeapon( self.riotshieldTakeWeapon );
|
||||
self.riotshieldTakeWeapon = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if( isValidNonShieldWeapon( prevWeapon ) )
|
||||
{
|
||||
self.lastNonShieldWeapon = prevWeapon;
|
||||
}
|
||||
}
|
||||
|
||||
updateRiotShieldAttachForNewWeapon( newWeapon );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
updateRiotShieldAttachForNewWeapon( newWeapon ) // self == player
|
||||
{
|
||||
// Do nothing, we want to keep that weapon on their arm. Will get another changeweapon when finished mantling
|
||||
if ( ( self IsMantling() ) && ( newWeapon == "none" ) )
|
||||
return;
|
||||
|
||||
updateFrontAndBackShields( newWeapon );
|
||||
}
|
||||
|
||||
|
||||
watchRiotshieldPickup() // self == player
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
self endon ( "track_riot_shield" );
|
||||
|
||||
self notify ( "watch_riotshield_pickup" );
|
||||
self endon ( "watch_riotshield_pickup" );
|
||||
|
||||
// tagTMR<NOTE>: fix for rare case when riotshield is given by the server
|
||||
// but the client fails to equip because of prone
|
||||
|
||||
self waittill ( "pickup_riotshield" );
|
||||
self endon ( "weapon_change" );
|
||||
|
||||
/#println( "Picked up riotshield, expecting weapon_change notify..." );#/
|
||||
|
||||
wait 0.5;
|
||||
|
||||
/#println( "picked up shield but didn't change weapons, attach it!" );#/
|
||||
|
||||
updateRiotShieldAttachForNewWeapon( self getCurrentWeapon() );
|
||||
}
|
||||
|
||||
|
||||
isValidNonShieldWeapon( weapon )
|
||||
{
|
||||
if( maps\mp\_utility::isKillstreakWeapon( weapon ) )
|
||||
return false;
|
||||
|
||||
if( weapon == "none" )
|
||||
return false;
|
||||
|
||||
if ( maps\mp\gametypes\_class::isValidEquipment( weapon, true ) ||
|
||||
maps\mp\gametypes\_class::isValidEquipment( weapon, false ) )
|
||||
return false;
|
||||
|
||||
if ( weaponIsRiotShield( weapon ) )
|
||||
return false;
|
||||
|
||||
if ( WeaponClass(weapon) == "ball" )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
startRiotshieldDeploy() // self == player
|
||||
{
|
||||
self thread watchRiotshieldDeploy();
|
||||
}
|
||||
|
||||
|
||||
handleRiotShieldShockPlant() // self == player
|
||||
{
|
||||
shield_ent = self.riotshieldEntity;
|
||||
assert( IsDefined( shield_ent ) );
|
||||
|
||||
min_damage = 10;
|
||||
max_damage = 50;
|
||||
radius = 150;
|
||||
radius_sq = ( radius * radius );
|
||||
|
||||
event_origin = self.riotshieldEntity.origin + ( 0, 0, -25 );
|
||||
|
||||
self RadiusDamage( event_origin, radius, max_damage, min_damage, self, "MOD_EXPLOSIVE" );
|
||||
|
||||
PlayFX( level._effect[ "riot_shield_shock_fx" ], event_origin, AnglesToForward( self.riotshieldEntity.angles + ( -90, 0, 0 )) );
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( isReallyAlive( player ) && !IsAlliedSentient( player, self ) )
|
||||
{
|
||||
if ( DistanceSquared( event_origin, player.origin ) < radius_sq )
|
||||
{
|
||||
player ShellShock( "concussion_grenade_mp", 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watchRiotshieldDeploy() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self notify( "start_riotshield_deploy" );
|
||||
self endon( "start_riotshield_deploy" );
|
||||
|
||||
self waittill( "startdeploy_riotshield" );
|
||||
|
||||
self PlaySound( "wpn_riot_shield_plant_mech" );
|
||||
|
||||
self waittill( "deploy_riotshield", deploy_attempt );
|
||||
|
||||
// if we have a deployed riotshield in the world, delete if we attempt to deploy another
|
||||
if ( IsDefined( self.riotshieldEntity ))
|
||||
{
|
||||
self.riotshieldEntity thread damageThenDestroyRiotshield();
|
||||
waitframe(); // give it some time to clean up
|
||||
}
|
||||
|
||||
curWeapon = self GetCurrentWeapon();
|
||||
self SetWeaponModelVariant( curWeapon, 0 );
|
||||
|
||||
shockVersion = weaponIsShockPlantRiotShield( curWeapon );
|
||||
|
||||
self PlaySound( "wpn_riot_shield_plant_punch" );
|
||||
if ( shockVersion )
|
||||
self PlaySound( "wpn_riot_shield_blast_punch" );
|
||||
|
||||
//self SetPlacementHint( 1 );
|
||||
|
||||
placement_hint = false;
|
||||
|
||||
if ( deploy_attempt )
|
||||
{
|
||||
placement = self CanPlaceRiotshield();
|
||||
|
||||
if ( placement["result"] && riotshieldDistanceTest( placement["origin"] ) )
|
||||
{
|
||||
zoffset = 28;
|
||||
|
||||
shield_ent = self spawnRiotshieldCover( placement["origin"] + ( 0, 0, zoffset ), placement["angles"] );
|
||||
coll_ent = self spawnRiotshieldCollision( placement["origin"] + ( 0, 0, zoffset ), placement["angles"], shield_ent );
|
||||
item_ent = DeployRiotShield( self, shield_ent );
|
||||
|
||||
primaries = self GetWeaponsListPrimaries();
|
||||
|
||||
/#
|
||||
assert( IsDefined( item_ent ) );
|
||||
assert( !IsDefined( self.riotshieldRetrieveTrigger ) );
|
||||
assert( !IsDefined( self.riotshieldEntity ) );
|
||||
assert( !IsDefined( self.riotshieldCollisionEntity ) );
|
||||
#/
|
||||
|
||||
self.riotshieldRetrieveTrigger = item_ent;
|
||||
self.riotshieldEntity = shield_ent;
|
||||
self.riotshieldCollisionEntity = coll_ent;
|
||||
|
||||
if ( shockVersion )
|
||||
self thread handleRiotShieldShockPlant();
|
||||
else
|
||||
PlayFXOnTag( getfx( "riot_shield_deploy_smoke" ), shield_ent, "tag_weapon" );
|
||||
|
||||
//shield_ent SetClientField( "riotshield_state", RIOTSHIELD_STATE_DEPLOYED );
|
||||
// TODO: spawn thread to: play deploy anim on shield, play dust fx at shield origin, wait 0.8, playfxontag light fx at tag_fx on shield
|
||||
shield_ent ScriptModelPlayAnimDeltaMotion( "npc_deployable_riotshield_stand_deploy" );
|
||||
// Play Dust FX at shield origin
|
||||
// spawn thread:
|
||||
thread spawnShieldLights( shield_ent );
|
||||
|
||||
|
||||
switchToKnife = false;
|
||||
if( self.lastNonShieldWeapon != "none" && self hasWeapon( self.lastNonShieldWeapon ) )
|
||||
self SwitchToWeaponImmediate( self.lastNonShieldWeapon );
|
||||
else if ( primaries.size > 0 )
|
||||
self SwitchToWeaponImmediate( primaries[0] );
|
||||
else
|
||||
switchToKnife = true;
|
||||
|
||||
if ( !self HasWeapon( "iw5_combatknife_mp" ) )
|
||||
{
|
||||
self GiveWeapon( "iw5_combatknife_mp" );
|
||||
self.riotshieldTakeWeapon = "iw5_combatknife_mp";
|
||||
}
|
||||
|
||||
if ( switchToKnife )
|
||||
self SwitchToWeaponImmediate( "iw5_combatknife_mp" );
|
||||
|
||||
// Moving platforms.
|
||||
data = SpawnStruct();
|
||||
data.deathOverrideCallback = ::damageThenDestroyRiotshield;
|
||||
shield_ent thread maps\mp\_movers::handle_moving_platforms( data );
|
||||
|
||||
self thread watchDeployedRiotshieldEnts();
|
||||
|
||||
self thread deleteShieldOnTriggerDeath( self.riotshieldRetrieveTrigger );
|
||||
self thread deleteShieldOnTriggerPickup( self.riotshieldRetrieveTrigger, self.riotshieldEntity );
|
||||
self thread deleteShieldOnPlayerDeathOrDisconnect( shield_ent );
|
||||
|
||||
self.riotshieldEntity thread watchDeployedRiotshieldDamage();
|
||||
level notify( "riotshield_planted", self );
|
||||
}
|
||||
else
|
||||
{
|
||||
placement_hint = true;
|
||||
|
||||
clip_max_ammo = WeaponClipSize( curWeapon );
|
||||
self setWeaponAmmoClip( curWeapon, clip_max_ammo );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// tagTMR<NOTE>: just lowering the shield not trying to deploy
|
||||
placement_hint = true;
|
||||
}
|
||||
|
||||
if ( placement_hint )
|
||||
{
|
||||
self SetRiotshieldFailHint();
|
||||
}
|
||||
}
|
||||
|
||||
spawnShieldLights(ent)
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
ent endon( "death" );
|
||||
|
||||
wait 0.6;
|
||||
PlayFXOnTag( getfx( "riot_shield_deploy_lights" ), ent, "tag_weapon" );
|
||||
}
|
||||
|
||||
riotshieldDistanceTest( origin )
|
||||
{
|
||||
/#
|
||||
assert ( IsDefined( origin ) );
|
||||
#/
|
||||
|
||||
min_dist_squared = GetDvarFloat( "riotshield_deploy_limit_radius" );
|
||||
min_dist_squared *= min_dist_squared;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( IsDefined( player.riotshieldEntity ) )
|
||||
{
|
||||
dist_squared = DistanceSquared( player.riotshieldEntity.origin, origin );
|
||||
if ( min_dist_squared > dist_squared )
|
||||
{
|
||||
/#
|
||||
println( "Shield placement denied! Failed distance check to other riotshields." );
|
||||
#/
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
spawnRiotshieldCover( origin, angles ) // self == player
|
||||
{
|
||||
shield_ent = Spawn( "script_model", origin );
|
||||
shield_ent.targetname = "riotshield_mp";
|
||||
shield_ent.angles = angles;
|
||||
|
||||
model = undefined;
|
||||
curWeapon = self GetCurrentPrimaryWeapon();
|
||||
if ( weaponIsRiotShield( curWeapon ) )
|
||||
model = GetWeaponModel( curWeapon );
|
||||
|
||||
if ( !IsDefined( model ) )
|
||||
model = "npc_deployable_riot_shield_base";
|
||||
|
||||
shield_ent SetModel( model );
|
||||
// shield_ent SetEntityOwner( self ); // Removing this as it causes traces from the player to pass through it unintentionally (grenades and missiles especially)
|
||||
shield_ent.owner = self;
|
||||
shield_ent.team = self.team;
|
||||
// shield_ent SetTeam( self.team );
|
||||
|
||||
// shield_ent UseAnimTree( #animtree );
|
||||
|
||||
return shield_ent;
|
||||
}
|
||||
|
||||
|
||||
spawnRiotshieldCollision( origin, angles, shield_ent ) // self == player
|
||||
{
|
||||
coll_ent = Spawn( "script_model", origin, 1 ); // third param is spawn flag for dynamic pathing
|
||||
coll_ent.targetname = "riotshield_coll_mp";
|
||||
coll_ent.angles = angles;
|
||||
coll_ent SetModel( "tag_origin" );
|
||||
coll_ent.owner = self;
|
||||
coll_ent.team = self.team;
|
||||
coll_ent CloneBrushmodelToScriptModel( level.riot_shield_collision );
|
||||
coll_ent DisconnectPaths();
|
||||
|
||||
return coll_ent;
|
||||
}
|
||||
|
||||
|
||||
watchDeployedRiotshieldEnts() // self == player
|
||||
{
|
||||
/#
|
||||
assert( IsDefined( self.riotshieldRetrieveTrigger ) );
|
||||
assert( IsDefined( self.riotshieldEntity ) );
|
||||
assert( IsDefined( self.riotshieldCollisionEntity ) );
|
||||
#/
|
||||
|
||||
self waittill( "destroy_riotshield" );
|
||||
|
||||
if ( IsDefined( self.riotshieldRetrieveTrigger ) )
|
||||
{
|
||||
self.riotshieldRetrieveTrigger delete();
|
||||
}
|
||||
|
||||
if ( IsDefined( self.riotshieldCollisionEntity ) )
|
||||
{
|
||||
self.riotshieldCollisionEntity ConnectPaths();
|
||||
self.riotshieldCollisionEntity delete();
|
||||
}
|
||||
|
||||
if ( IsDefined( self.riotshieldEntity ) )
|
||||
{
|
||||
self.riotshieldEntity delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
deleteShieldOnTriggerPickup( shield_trigger, shield_ent ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
shield_trigger endon( "death" );
|
||||
|
||||
shield_trigger waittill( "trigger", player );
|
||||
|
||||
// Transfer any linked entities from the deployed shield to the player's arm at the same offset
|
||||
HandlePickupDeployedRiotshield( player, shield_ent );
|
||||
|
||||
self notify( "destroy_riotshield" );
|
||||
}
|
||||
|
||||
|
||||
deleteShieldOnTriggerDeath( shield_trigger ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
shield_trigger waittill( "death" );
|
||||
self notify( "destroy_riotshield" );
|
||||
}
|
||||
|
||||
|
||||
deleteShieldOnPlayerDeathOrDisconnect( shield_ent ) // self == player
|
||||
{
|
||||
shield_ent endon( "death" );
|
||||
shield_ent endon( "damageThenDestroyRiotshield" );
|
||||
|
||||
self waittill_any( "death", "disconnect", "remove_planted_weapons" );
|
||||
|
||||
shield_ent thread damageThenDestroyRiotshield();
|
||||
}
|
||||
|
||||
|
||||
watchDeployedRiotshieldDamage() // self == riotshield script_model ent
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
damageMax = GetDvarInt( "riotshield_deployed_health" );
|
||||
self.damageTaken = 0;
|
||||
|
||||
nextDamageAnimTime = 0;
|
||||
|
||||
while( true )
|
||||
{
|
||||
self.maxhealth = 100000;
|
||||
self.health = self.maxhealth;
|
||||
|
||||
self waittill( "damage", damage, attacker, direction, point, type, modelName, tagName, partname, iDFlags, weaponName );
|
||||
|
||||
if( !isdefined( attacker ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/#
|
||||
assert( isDefined( self.owner ) && isDefined( self.owner.team ));
|
||||
#/
|
||||
|
||||
if ( isplayer( attacker ) )
|
||||
{
|
||||
if ( ( level.teamBased ) && ( attacker.team == self.owner.team ) && ( attacker != self.owner ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
isMeleeDamage = false;
|
||||
isBulletDamage = false;
|
||||
|
||||
if ( isMeleeMOD( type ) )
|
||||
{
|
||||
isMeleeDamage = true;
|
||||
damage *= GetDvarfloat( "riotshield_melee_damage_scale" );
|
||||
}
|
||||
else if ( type == "MOD_PISTOL_BULLET" || type == "MOD_RIFLE_BULLET" )
|
||||
{
|
||||
isBulletDamage = true;
|
||||
damage *= GetDvarfloat( "riotshield_bullet_damage_scale" );
|
||||
}
|
||||
else if ( type == "MOD_GRENADE" || type == "MOD_GRENADE_SPLASH" || type == "MOD_EXPLOSIVE" || type == "MOD_EXPLOSIVE_SPLASH" || type == "MOD_PROJECTILE" || type == "MOD_PROJECTILE_SPLASH")
|
||||
{
|
||||
damage *= GetDvarfloat( "riotshield_explosive_damage_scale" );
|
||||
}
|
||||
else if ( type == "MOD_IMPACT" )
|
||||
{
|
||||
damage *= GetDvarFloat( "riotshield_projectile_damage_scale" );
|
||||
}
|
||||
else if ( type == "MOD_CRUSH" )
|
||||
{
|
||||
damage = damageMax;
|
||||
}
|
||||
|
||||
self.damageTaken += damage;
|
||||
|
||||
if( self.damageTaken >= damageMax )
|
||||
{
|
||||
self thread damageThenDestroyRiotshield( attacker, weaponName );
|
||||
break;
|
||||
}
|
||||
else if ( ( isMeleeDamage || isBulletDamage ) && ( GetTime() >= nextDamageAnimTime ) )
|
||||
{
|
||||
nextDamageAnimTime = GetTime() + 500;
|
||||
|
||||
fromBack = false;
|
||||
shield_fwd = AnglesToForward( self.angles );
|
||||
if ( VectorDot( direction, shield_fwd ) > 0 )
|
||||
fromBack = true;
|
||||
|
||||
if ( isMeleeDamage )
|
||||
{
|
||||
if ( fromBack )
|
||||
self ScriptModelPlayAnimDeltaMotion( "npc_deployable_riotshield_stand_melee_back" );
|
||||
else
|
||||
self ScriptModelPlayAnimDeltaMotion( "npc_deployable_riotshield_stand_melee_front" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( isBulletDamage );
|
||||
if ( fromBack )
|
||||
self ScriptModelPlayAnimDeltaMotion( "npc_deployable_riotshield_stand_shot_back" );
|
||||
else
|
||||
self ScriptModelPlayAnimDeltaMotion( "npc_deployable_riotshield_stand_shot" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
damageThenDestroyRiotshield(attacker, weaponName) // self == riotshield script_model ent
|
||||
{
|
||||
self notify( "damageThenDestroyRiotshield" );
|
||||
self endon( "death" );
|
||||
|
||||
if ( IsDefined( self.owner.riotshieldRetrieveTrigger ) )
|
||||
{
|
||||
self.owner.riotshieldRetrieveTrigger delete();
|
||||
}
|
||||
|
||||
if ( IsDefined( self.owner.riotshieldCollisionEntity ) )
|
||||
{
|
||||
self.owner.riotshieldCollisionEntity ConnectPaths();
|
||||
self.owner.riotshieldCollisionEntity delete();
|
||||
}
|
||||
|
||||
|
||||
self.owner.riotshieldEntity = undefined;
|
||||
|
||||
self NotSolid();
|
||||
// self SetClientField( "riotshield_state", RIOTSHIELD_STATE_DESTROYED );
|
||||
// TODO: stop fx, play sound, play destroyed anim, wait, force not simple dobj...?
|
||||
// Stop FX on shield
|
||||
// Play destroyed sound
|
||||
self ScriptModelPlayAnimDeltaMotion( "npc_deployable_riotshield_stand_destroyed" );
|
||||
// Wait
|
||||
// Force not simple dobj?
|
||||
|
||||
// if (isdefined (attacker) && isdefined (weaponName) && attacker != self.owner && isplayer( attacker ) )
|
||||
// {
|
||||
// maps\mp\_scoreevents::processScoreEvent( "destroyed_shield", attacker, self.owner, weaponName );
|
||||
// }
|
||||
|
||||
wait( GetDvarFloat( "riotshield_destroyed_cleanup_time" ) );
|
||||
|
||||
self delete();
|
||||
}
|
||||
|
||||
|
||||
watchRiotshieldStuckEntityDeath( grenade, owner ) // self == entity stuck with nade
|
||||
{
|
||||
grenade endon( "death" );
|
||||
|
||||
self waittill_any( "damageThenDestroyRiotshield", "death", "disconnect", "weapon_change", "deploy_riotshield" );
|
||||
|
||||
grenade Detonate( owner );
|
||||
}
|
202
raw/maps/mp/_scoreboard.gsc
Normal file
202
raw/maps/mp/_scoreboard.gsc
Normal file
@ -0,0 +1,202 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
processLobbyScoreboards()
|
||||
{
|
||||
foreach ( player in level.placement["all"] )
|
||||
player setPlayerScoreboardInfo();
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
buildScoreboardType( "multiteam" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
player setCommonPlayerData( "round", "scoreboardType", "multiteam" );
|
||||
|
||||
setClientMatchData( "alliesScore", -1 );
|
||||
setClientMatchData( "axisScore", -1 );
|
||||
}
|
||||
else if ( level.teamBased )
|
||||
{
|
||||
alliesScore = getTeamScore( "allies" );
|
||||
axisScore = getTeamScore( "axis" );
|
||||
|
||||
if ( alliesScore == axisScore )
|
||||
winner = "tied";
|
||||
else if ( alliesScore > axisScore )
|
||||
winner = "allies";
|
||||
else
|
||||
winner = "axis";
|
||||
|
||||
setClientMatchData( "alliesScore", alliesScore );
|
||||
setClientMatchData( "axisScore", axisScore );
|
||||
|
||||
if ( winner == "tied" )
|
||||
{
|
||||
// build both, assign type to your team
|
||||
buildScoreboardType( "allies" );
|
||||
buildScoreboardType( "axis" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player_pers_team = player.pers["team"];
|
||||
if ( !IsDefined(player_pers_team) )
|
||||
continue;
|
||||
|
||||
if ( player_pers_team == "spectator" )
|
||||
player setCommonPlayerData( "round", "scoreboardType", "allies" );
|
||||
else
|
||||
player setCommonPlayerData( "round", "scoreboardType", player_pers_team );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// build just winner, assign type to winner
|
||||
buildScoreboardType( winner );
|
||||
|
||||
foreach ( player in level.players )
|
||||
player setCommonPlayerData( "round", "scoreboardType", winner );
|
||||
}
|
||||
}
|
||||
else // not teambased
|
||||
{
|
||||
buildScoreboardType( "neutral" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
player setCommonPlayerData( "round", "scoreboardType", "neutral" );
|
||||
|
||||
setClientMatchData( "alliesScore", -1 );
|
||||
setClientMatchData( "axisScore", -1 );
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
//log xp gains
|
||||
totalXp = 0;
|
||||
if ( !player rankingEnabled() || practiceRoundGame() )
|
||||
totalXp = player.pers["summary"][ "xp" ];
|
||||
else
|
||||
totalXp = ( player GetTotalMpXp() - player.pers["summary"][ "matchStartXp" ] );
|
||||
|
||||
player setCommonPlayerData( "round", "totalXp", totalXp );
|
||||
player setCommonPlayerData( "round", "scoreXp", player.pers["summary"]["score"] );
|
||||
player setCommonPlayerData( "round", "challengeXp", player.pers["summary"]["challenge"] );
|
||||
player setCommonPlayerData( "round", "matchXp", player.pers["summary"]["match"] );
|
||||
player setCommonPlayerData( "round", "miscXp", player.pers["summary"]["misc"] );
|
||||
player setCommonPlayerData( "round", "entitlementXp", player.pers["summary"]["entitlementXP"] );
|
||||
player setCommonPlayerData( "round", "clanWarsXp", player.pers["summary"]["clanWarsXP"] );
|
||||
}
|
||||
}
|
||||
|
||||
setPlayerScoreboardInfo()
|
||||
{
|
||||
scoreboardPlayerCount = getClientMatchData( "scoreboardPlayerCount" );
|
||||
if ( scoreboardPlayerCount <= 24 ) //MaxPlayers
|
||||
{
|
||||
setClientMatchData( "players", self.clientMatchDataId, "score", self.pers["score"] );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][score]: " + self.pers["score"] );
|
||||
|
||||
kills = self.pers["kills"];
|
||||
|
||||
setClientMatchData( "players", self.clientMatchDataId, "kills", kills );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][kills]: " + kills );
|
||||
|
||||
if ( level.gameType == "ctf" || level.gameType == "sr" || level.gameType == "gun" )
|
||||
{
|
||||
// ctf stores flag returns in assists
|
||||
// sr stores rescues in assists
|
||||
// gun stores stabs in assists
|
||||
assists = self.assists;
|
||||
}
|
||||
else
|
||||
{
|
||||
assists = self.pers["assists"];
|
||||
}
|
||||
setClientMatchData( "players", self.clientMatchDataId, "assists", assists );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][assists]: " + assists );
|
||||
|
||||
deaths = self.pers["deaths"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "deaths", deaths );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][deaths]: " + deaths );
|
||||
|
||||
headshots = self.pers["headshots"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "headshots", headshots );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][headshots]: " + headshots );
|
||||
|
||||
team = self.pers["team"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "team", team );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][team]: " + team );
|
||||
|
||||
faction = game[self.pers["team"]];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "faction", faction );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][faction]: " + faction );
|
||||
|
||||
extrascore0 = self.pers["extrascore0"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "extrascore0", extrascore0 );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][extrascore0]: " + extrascore0 );
|
||||
|
||||
extrascore1 = self.pers["extrascore1"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "extrascore1", extrascore1 );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][extrascore1]: " + extrascore1 );
|
||||
|
||||
println( "Scoreboard: scoreboardPlayerCount was " + scoreboardPlayerCount );
|
||||
scoreboardPlayerCount++;
|
||||
setClientMatchData( "scoreboardPlayerCount", scoreboardPlayerCount );
|
||||
println( "Scoreboard: scoreboardPlayerCount now " + scoreboardPlayerCount );
|
||||
}
|
||||
else
|
||||
{
|
||||
println( "Scoreboard: scoreboardPlayerCount is greater than 24 (" + scoreboardPlayerCount + ")" );
|
||||
}
|
||||
}
|
||||
|
||||
buildScoreboardType( team )
|
||||
{
|
||||
assert( team == "allies" || team == "axis" || team == "neutral" || team == "multiteam" );
|
||||
|
||||
println( "Scoreboard: Building scoreboard (" + team + ")" );
|
||||
|
||||
if ( team == "multiteam" )
|
||||
{
|
||||
index = 0;
|
||||
|
||||
foreach( teamname in level.teamNameList )
|
||||
{
|
||||
foreach ( player in level.placement[teamname] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", "multiteam", index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.clientMatchDataId + "]" );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( team == "neutral" )
|
||||
{
|
||||
index = 0;
|
||||
foreach ( player in level.placement["all"] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", team, index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.clientMatchDataId + "]" );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
otherTeam = getOtherTeam( team );
|
||||
|
||||
index = 0;
|
||||
foreach ( player in level.placement[team] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", team, index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.name + "(" + player.clientMatchDataId + ")]" );
|
||||
index++;
|
||||
}
|
||||
|
||||
foreach ( player in level.placement[otherTeam] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", team, index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.name + "(" + player.clientMatchDataId + ")]" );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
179
raw/maps/mp/_shutter.gsc
Normal file
179
raw/maps/mp/_shutter.gsc
Normal file
@ -0,0 +1,179 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
main()
|
||||
{
|
||||
// thread windController();
|
||||
level.inc = 0;
|
||||
|
||||
array_levelthread (getentarray("wire","targetname"), ::wireWander);
|
||||
|
||||
leftShutters = getentarray ("shutter_left","targetname");
|
||||
addShutters = getentarray ("shutter_right_open","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
leftShutters[leftShutters.size] = addShutters[i];
|
||||
addShutters = getentarray ("shutter_left_closed","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
leftShutters[leftShutters.size] = addShutters[i];
|
||||
|
||||
for (i=0;i<leftShutters.size;i++)
|
||||
{
|
||||
shutter = leftShutters[i];
|
||||
shutter rotateto((shutter.angles[0], shutter.angles[1] + 180, shutter.angles[2]), 0.1);
|
||||
}
|
||||
wait (0.2);
|
||||
|
||||
for (i=0;i<leftShutters.size;i++)
|
||||
leftShutters[i].startYaw = leftShutters[i].angles[1];
|
||||
|
||||
rightShutters = getentarray ("shutter_right","targetname");
|
||||
addShutters = getentarray ("shutter_left_open","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
rightShutters[rightShutters.size] = addShutters[i];
|
||||
addShutters = getentarray ("shutter_right_closed","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
rightShutters[rightShutters.size] = addShutters[i];
|
||||
|
||||
for (i=0;i<rightShutters.size;i++)
|
||||
rightShutters[i].startYaw = rightShutters[i].angles[1];
|
||||
|
||||
addShutters = undefined;
|
||||
|
||||
windDirection = "left";
|
||||
for (;;)
|
||||
{
|
||||
array_levelthread (leftShutters, ::shutterWanderLeft, windDirection);
|
||||
array_levelthread (rightShutters, ::shutterWanderRight, windDirection);
|
||||
level waittill ("wind blows", windDirection);
|
||||
}
|
||||
}
|
||||
|
||||
windController()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
windDirection = "left";
|
||||
if (randomint(100) > 50)
|
||||
windDirection = "right";
|
||||
level notify ("wind blows", windDirection);
|
||||
wait (2 + randomfloat(10));
|
||||
}
|
||||
}
|
||||
|
||||
shutterWanderLeft(shutter, windDirection)
|
||||
{
|
||||
// println ("shutter angles ", shutter.angles[1]);
|
||||
// assert (shutter.angles[1] >= shutter.startYaw);
|
||||
// assert (shutter.angles[1] < shutter.startYaw + 180);
|
||||
|
||||
// println ("Wind + ", level.inc);
|
||||
level.inc++;
|
||||
level endon ("wind blows");
|
||||
|
||||
newYaw = shutter.startYaw;
|
||||
if (windDirection == "left")
|
||||
newYaw += 179.9;
|
||||
|
||||
newTime = 0.2;
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime);
|
||||
wait (newTime + 0.1);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rot = randomint(80);
|
||||
if (randomint(100) > 50)
|
||||
rot *= -1;
|
||||
|
||||
newYaw = shutter.angles[1] + rot;
|
||||
altYaw = shutter.angles[1] + (rot*-1);
|
||||
if ((newYaw < shutter.startYaw) || (newYaw > shutter.startYaw + 179))
|
||||
{
|
||||
newYaw = altYaw;
|
||||
}
|
||||
|
||||
dif = abs(shutter.angles[1] - newYaw);
|
||||
|
||||
newTime = dif*0.02 + randomfloat(2);
|
||||
if (newTime < 0.3)
|
||||
newTime = 0.3;
|
||||
// println ("startyaw " + shutter.startyaw + " newyaw " + newYaw);
|
||||
|
||||
// assert (newYaw >= shutter.startYaw);
|
||||
// assert (newYaw < shutter.startYaw + 179);
|
||||
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime, newTime * 0.5, newTime * 0.5);
|
||||
wait (newTime);
|
||||
}
|
||||
}
|
||||
|
||||
shutterWanderRight(shutter, windDirection)
|
||||
{
|
||||
// println ("shutter angles ", shutter.angles[1]);
|
||||
// assert (shutter.angles[1] >= shutter.startYaw);
|
||||
// assert (shutter.angles[1] < shutter.startYaw + 180);
|
||||
|
||||
// println ("Wind + ", level.inc);
|
||||
level.inc++;
|
||||
level endon ("wind blows");
|
||||
|
||||
newYaw = shutter.startYaw;
|
||||
if (windDirection == "left")
|
||||
newYaw += 179.9;
|
||||
|
||||
newTime = 0.2;
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime);
|
||||
wait (newTime + 0.1);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rot = randomint(80);
|
||||
if (randomint(100) > 50)
|
||||
rot *= -1;
|
||||
|
||||
newYaw = shutter.angles[1] + rot;
|
||||
altYaw = shutter.angles[1] + (rot*-1);
|
||||
if ((newYaw < shutter.startYaw) || (newYaw > shutter.startYaw + 179))
|
||||
{
|
||||
newYaw = altYaw;
|
||||
}
|
||||
|
||||
dif = abs(shutter.angles[1] - newYaw);
|
||||
|
||||
newTime = dif*0.02 + randomfloat(2);
|
||||
if (newTime < 0.3)
|
||||
newTime = 0.3;
|
||||
// println ("startyaw " + shutter.startyaw + " newyaw " + newYaw);
|
||||
|
||||
// assert (newYaw >= shutter.startYaw);
|
||||
// assert (newYaw < shutter.startYaw + 179);
|
||||
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime, newTime * 0.5, newTime * 0.5);
|
||||
wait (newTime);
|
||||
}
|
||||
}
|
||||
|
||||
wireWander (wire)
|
||||
{
|
||||
origins = getentarray (wire.target,"targetname");
|
||||
org1 = origins[0].origin;
|
||||
org2 = origins[1].origin;
|
||||
|
||||
angles = vectortoangles (org1 - org2);
|
||||
ent = spawn ("script_model",(0,0,0));
|
||||
ent.origin = ( org1 * 0.5 ) + ( org2 * 0.5 );
|
||||
// ent setmodel ("temp");
|
||||
ent.angles = angles;
|
||||
wire linkto (ent);
|
||||
rottimer = 2;
|
||||
rotrange = 0.9;
|
||||
dist = 4 + randomfloat(2);
|
||||
ent rotateroll(dist*0.5,0.2);
|
||||
wait (0.2);
|
||||
for (;;)
|
||||
{
|
||||
rottime = rottimer + randomfloat (rotRange) - (rotRange * 0.5);
|
||||
ent rotateroll(dist,rottime, rottime*0.5, rottime*0.5);
|
||||
wait (rottime);
|
||||
ent rotateroll(dist * -1,rottime, rottime*0.5, rottime*0.5);
|
||||
wait (rottime);
|
||||
}
|
||||
}
|
394
raw/maps/mp/_snd_common_mp.gsc
Normal file
394
raw/maps/mp/_snd_common_mp.gsc
Normal file
@ -0,0 +1,394 @@
|
||||
#include common_scripts\utility;
|
||||
#include common_scripts\_exploder;
|
||||
#include maps\mp\_audio;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// _snd_common_mp.gsc
|
||||
//
|
||||
// This scriptfile is intended for common scripting for blacksmith (S1).
|
||||
// This file is for sound designers to add message handlers or for minor
|
||||
// prototype scripting systems, etc. Core systems go in their own files and
|
||||
// need to be approved by the audio engineer and audio director.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
init()
|
||||
{
|
||||
snd_message_init();
|
||||
register_common_mp_snd_messages();
|
||||
thread snd_mp_mix_init();
|
||||
}
|
||||
|
||||
snd_mp_mix_init()
|
||||
{
|
||||
level._snd.dynamic_event_happened = false;
|
||||
|
||||
// Set the Dyanmic Event Mix State to False by Default.
|
||||
//level._snd.dynamic_event_happened = false;
|
||||
|
||||
// Setup Pre-Event Mix for Every Player that is present when the match starts.
|
||||
if(Isdefined (level.players) && level.players.size > 0)
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
AssertEx( isdefined( player ), "isdefined( player )" );
|
||||
player clientaddsoundsubmix( "mp_init_mix" );
|
||||
wait( 0.05 );
|
||||
player clientaddsoundsubmix( "mp_pre_event_mix" );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
snd_mp_mix_post_event()
|
||||
{
|
||||
level._snd.dynamic_event_happened = true;
|
||||
if(Isdefined (level.players) && level.players.size > 0)
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
AssertEx( isdefined( player ), "isdefined( player )" );
|
||||
player clientclearsoundsubmix( "mp_pre_event_mix" );
|
||||
wait(0.05);
|
||||
player clientaddsoundsubmix( "mp_post_event_mix" );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
snd_mp_player_join()
|
||||
{
|
||||
self clientaddsoundsubmix( "mp_init_mix" );
|
||||
if ( !isDefined(level._snd.dynamic_event_happened) || !level._snd.dynamic_event_happened)
|
||||
{
|
||||
self clientaddsoundsubmix( "mp_pre_event_mix" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self clientclearsoundsubmix( "mp_pre_event_mix" );
|
||||
self clientaddsoundsubmix( "mp_post_event_mix" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// SOUND MESSAGE
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
snd_message_init()
|
||||
{
|
||||
if(!IsDefined(level._snd))
|
||||
level._snd = SpawnStruct();
|
||||
|
||||
if(!IsDefined( level._snd.messages ))
|
||||
level._snd.messages = [];
|
||||
}
|
||||
|
||||
snd_register_message( message, callback )
|
||||
{
|
||||
assertEx( IsDefined( level._snd ), "Need to call snd_message_init() before calling this function." );
|
||||
assert( IsArray( level._snd.messages ) );
|
||||
level._snd.messages[message] = callback;
|
||||
}
|
||||
|
||||
snd_music_message( message, arg1, arg2 )
|
||||
{
|
||||
level notify( "stop_other_music" );
|
||||
level endon( "stop_other_music" );
|
||||
|
||||
if ( IsDefined( arg2 ) )
|
||||
childthread snd_message( "snd_music_handler", message, arg1, arg2 );
|
||||
else if ( IsDefined( arg1 ) )
|
||||
childthread snd_message( "snd_music_handler", message, arg1 );
|
||||
else
|
||||
childthread snd_message( "snd_music_handler", message );
|
||||
}
|
||||
|
||||
snd_message( message, arg1, arg2, arg3 )
|
||||
{
|
||||
AssertEx( IsDefined( level._snd ), "Need to call snd_message_init() before calling this function." );
|
||||
Assert( IsArray( level._snd.messages ) );
|
||||
|
||||
if ( IsDefined( level._snd.messages[message] ) )
|
||||
{
|
||||
if ( IsDefined( arg3 ) )
|
||||
thread [[ level._snd.messages[message] ]]( arg1, arg2, arg3 );
|
||||
else if ( IsDefined( arg2 ) )
|
||||
thread [[ level._snd.messages[message] ]]( arg1, arg2 );
|
||||
else if ( IsDefined( arg1 ) )
|
||||
thread [[ level._snd.messages[message] ]]( arg1 );
|
||||
else
|
||||
thread [[ level._snd.messages[message] ]]();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// COMMON MESSAGE HANDLERS
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
register_common_mp_snd_messages()
|
||||
{
|
||||
////////////////////////
|
||||
//EXO ABILITIES
|
||||
////////////////////////
|
||||
|
||||
//EXO CLOAK
|
||||
snd_register_message( "mp_exo_cloak_activate", ::mp_exo_cloak_activate );
|
||||
snd_register_message( "mp_exo_cloak_deactivate", ::mp_exo_cloak_deactivate );
|
||||
|
||||
//EXO HEALTH
|
||||
snd_register_message( "mp_exo_health_activate", ::mp_exo_health_activate );
|
||||
snd_register_message( "mp_exo_health_deactivate", ::mp_exo_health_deactivate );
|
||||
|
||||
//EXO HOVER
|
||||
snd_register_message( "mp_regular_exo_hover", ::mp_regular_exo_hover );
|
||||
snd_register_message( "mp_suppressed_exo_hover", ::mp_suppressed_exo_hover );
|
||||
|
||||
//EXO MUTE
|
||||
snd_register_message( "mp_exo_mute_activate", ::mp_exo_mute_activate );
|
||||
snd_register_message( "mp_exo_mute_deactivate", ::mp_exo_mute_deactivate );
|
||||
|
||||
//EXO OVERCLOCK
|
||||
snd_register_message( "mp_exo_overclock_activate", ::mp_exo_overclock_activate );
|
||||
snd_register_message( "mp_exo_overclock_deactivate", ::mp_exo_overclock_deactivate );
|
||||
|
||||
//EXO PING
|
||||
snd_register_message( "mp_exo_ping_activate", ::mp_exo_ping_activate );
|
||||
snd_register_message( "mp_exo_ping_deactivate", ::mp_exo_ping_deactivate );
|
||||
|
||||
//EXO REPULSOR
|
||||
snd_register_message( "mp_exo_repulsor_activate", ::mp_exo_repulsor_activate );
|
||||
snd_register_message( "mp_exo_repulsor_deactivate", ::mp_exo_repulsor_deactivate );
|
||||
snd_register_message( "mp_exo_repulsor_repel", ::mp_exo_repulsor_repel );
|
||||
|
||||
//EXO SHIELD
|
||||
snd_register_message( "mp_exo_shield_activate", ::mp_exo_shield_activate );
|
||||
snd_register_message( "mp_exo_shield_deactivate", ::mp_exo_shield_deactivate );
|
||||
|
||||
////////////////////////
|
||||
// KILLSTREAKS
|
||||
////////////////////////
|
||||
|
||||
//GOLIATH
|
||||
snd_register_message( "goliath_pod_burst", ::mp_ks_goliath_pod_burst );
|
||||
snd_register_message( "goliath_death_explosion", ::mp_ks_goliath_death_explosion );
|
||||
snd_register_message( "goliath_self_destruct", ::mp_ks_goliath_self_destruct );
|
||||
|
||||
|
||||
////////////////////////
|
||||
// WEAPONS
|
||||
////////////////////////
|
||||
|
||||
//EXO KNIFE
|
||||
snd_register_message( "exo_knife_player_impact", ::mp_wpn_exo_knife_player_impact );
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//EXO CLOAK
|
||||
////////////////////////
|
||||
mp_exo_cloak_activate()
|
||||
{
|
||||
//iprintlnbold( "EXO CLOAK ON" );
|
||||
self PlaySound( "mp_exo_cloak_activate" );
|
||||
}
|
||||
|
||||
mp_exo_cloak_deactivate()
|
||||
{
|
||||
//iprintlnbold( "EXO CLOAK OFF" );
|
||||
self PlaySound( "mp_exo_cloak_deactivate" );
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//EXO HEALTH
|
||||
////////////////////////
|
||||
mp_exo_health_activate()
|
||||
{
|
||||
self PlaySound( "mp_exo_stim_activate" ); // self = player
|
||||
}
|
||||
|
||||
mp_exo_health_deactivate()
|
||||
{
|
||||
self PlaySound( "mp_exo_stim_deactivate" ); // self = player
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//EXO HOVER
|
||||
////////////////////////
|
||||
mp_regular_exo_hover()
|
||||
{
|
||||
// Activate Hover
|
||||
self PlaylocalSound( "mp_exo_hover_activate" );
|
||||
self PlaylocalSound( "mp_exo_hover_fuel" );
|
||||
|
||||
self waittill( "stop_exo_hover_effects" );
|
||||
|
||||
// Deactivate
|
||||
self PlaylocalSound( "mp_exo_hover_deactivate" );
|
||||
self StopLocalSound( "mp_exo_hover_sup_fuel" );
|
||||
}
|
||||
|
||||
mp_suppressed_exo_hover()
|
||||
{
|
||||
// Activate Hover
|
||||
self PlaylocalSound( "mp_exo_hover_sup_activate" );
|
||||
self PlaylocalSound( "mp_exo_hover_sup_fuel" );
|
||||
|
||||
self waittill( "stop_exo_hover_effects" );
|
||||
|
||||
// Deactivate
|
||||
self PlaylocalSound( "mp_exo_hover_sup_deactivate" );
|
||||
self StopLocalSound( "mp_exo_hover_sup_fuel" );
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
//EXO MUTE DEVICE
|
||||
////////////////////////
|
||||
mp_exo_mute_activate()
|
||||
{
|
||||
self PlayLocalSound( "mp_exo_mute_activate" ); // self = player
|
||||
}
|
||||
|
||||
mp_exo_mute_deactivate()
|
||||
{
|
||||
self PlayLocalSound( "mp_exo_mute_deactivate" ); // self = player
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//EXO OVERCLOCK
|
||||
////////////////////////
|
||||
mp_exo_overclock_activate()
|
||||
{
|
||||
//self PlaySound( "mp_exo_overclock_bed" );
|
||||
self PlaySound( "mp_exo_overclock_activate" );
|
||||
}
|
||||
|
||||
mp_exo_overclock_deactivate()
|
||||
{
|
||||
//self PlaySound( "mp_exo_overclock_bed" );
|
||||
self PlaySound( "mp_exo_overclock_deactivate" );
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//EXO PING
|
||||
////////////////////////
|
||||
mp_exo_ping_activate()
|
||||
{
|
||||
self PlayLocalSound( "mp_exo_ping_activate" );
|
||||
//self PlayLocalSound( "mp_exo_ping_bed" );
|
||||
}
|
||||
|
||||
mp_exo_ping_deactivate()
|
||||
{
|
||||
//self StopLocalSound( "mp_exo_ping_bed" );
|
||||
self PlaySound( "mp_exo_ping_deactivate" );
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//EXO REPULSOR
|
||||
////////////////////////
|
||||
mp_exo_repulsor_activate()
|
||||
{
|
||||
self PlaySound( "mp_exo_trophy_activate" );
|
||||
//self PlayLocalSound( "mp_exo_trophy_bed" );
|
||||
}
|
||||
|
||||
mp_exo_repulsor_deactivate()
|
||||
{
|
||||
//self PlaySound( "mp_exo_trophy_bed" );
|
||||
self PlaySound( "mp_exo_trophy_deactivate" );
|
||||
}
|
||||
|
||||
mp_exo_repulsor_repel()
|
||||
{
|
||||
playSoundAtPos( self.origin, "mp_exo_trophy_intercept" );
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//EXO SHIELD
|
||||
////////////////////////
|
||||
mp_exo_shield_activate()
|
||||
{
|
||||
self PlaySound( "mp_exo_shield_activate" );
|
||||
//playSoundAtPos( self.origin, "mp_exo_shield_activate_npc" ); // self = player
|
||||
}
|
||||
|
||||
mp_exo_shield_deactivate()
|
||||
{
|
||||
self PlaySound( "mp_exo_shield_deactivate" );
|
||||
//playSoundAtPos( self.origin, "mp_exo_shield_deactivate_npc" );
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//WEAPONS
|
||||
////////////////////////
|
||||
|
||||
mp_wpn_exo_knife_player_impact()
|
||||
{
|
||||
playSoundAtPos( self.origin, "wpn_combatknife_stab_npc" );
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
//KILLSTREAKS
|
||||
////////////////////////
|
||||
mp_ks_goliath_pod_burst()
|
||||
{
|
||||
self PlayLocalSound( "goliath_suit_up_pod_burst" );
|
||||
}
|
||||
|
||||
mp_ks_goliath_death_explosion()
|
||||
{
|
||||
self playsound( "goliath_death_destruct" );
|
||||
}
|
||||
|
||||
mp_ks_goliath_self_destruct()
|
||||
{
|
||||
self playsound( "goliath_death_destruct" );
|
||||
}
|
||||
|
||||
/* END OF FILE /////////////////////////////////////////////////////////////////
|
||||
880888888880.G@888@888888888;t88GL808888Gf00CC00LC00888880088ft88888G88808808808
|
||||
8888@@@88888.L@8@@888888@880.f88L88888880f00G8CG00088800888000LC0888888808808888
|
||||
888@@@@88888G088@@888888;:f.i@888f08888GtC0G001000000000088GG8CCG888888800808888
|
||||
888@@@@088880t,.10@@@@801.,C88888888GG080G8800000000000000GtC0LLfG88888888888888
|
||||
088@@@@008@888GfttL1i1tC8888888GG088888888800C0G00000000CCGG0GL1tt88888888888888
|
||||
888@@@88CG888888888888888@88@L088888888888880GG0G0000000000GCL1:i108888888888888
|
||||
@88@@@888CtL88888888888888888t88888888088888GtLG00CCGGGGGGGCL1..:1C8880888888888
|
||||
@8@@@@8880GGftC088880LLL0888880888888CG80080CfGGGG080CLfLLfi.,..:1G8888888888888
|
||||
@@@@@@888800GGGGC888888808888888888880000GG0CLLfffG0888ffLf1i,..,iC8888888888888
|
||||
@@@@@@88@888888800t88888CG8888888888080000GCCLf1tLGCG888GGCf1: .,iG0880888888888
|
||||
@@@@@@@@8888800000iCG08088888888888808G0GGGCCfLfLLLLt10880GLfLGC1L0088G088888888
|
||||
@@@8@@@@8888800000GfttC88888888888888888000GGCCf1fttf1:i8000G00GfC00080888888888
|
||||
@@@@@@@@8888800000008888888888@@888888888888888800GCL1;:,,fLCfLLfG00008880888888
|
||||
8@@@@@@@@88800000000888@88888888888888888800GGG008888Ci;;;i1it8Cf000008088888888
|
||||
88@@@@@@@8888800088888888888888888888000GGCCLLLLLLCC088811it0888G888888080888888
|
||||
@@@@@@@@@8888888888888888888888000GGCCCLLfftfLCCfttffLG8088088888088888008880000
|
||||
@@88@@@8@88888008888@@888880000GGCLLLfffttffft1iii;iitLC008G888888888GLt1088880G
|
||||
8888888888888888888888@@8888880GGCLLffffft111tfCCCCGGG0000G08888888880LfC08880GG
|
||||
@888888@88888808888888@808888888880CLfC8888888888880000GCCLC08888888801LC08800CL
|
||||
@88888888888880888888880GG80CG00G0Liii1L0888880GfffffftttLGGG88888888CfCG080CCGf
|
||||
@8888808888888888800000GGCLLCLtt1ii:,,:;1t1tt1iiii11iiii1L00088088880fLL008Cff11
|
||||
8888080888888888880GCLfffft11t1;::;:..:;i;,,,:;;;;::,::itC0C08088880GLC8800C1tCt
|
||||
8888000088888888080CLt1i;:;::,,,::::..,;i;:,........,,;ifC0C080800f08L88800GtfGL
|
||||
@@8800G080888888080Cf1i;:,,,,.,,:;:,..,;i1i:,......,,:itCG000G0001CGC088888GtfCf
|
||||
@@88888080000888800Cf1;::,,,,,,,;;,,..,;i1t1:,..,,,:;itLG0000GCGGLGL088888880000
|
||||
@@88800008000808888GL1;;:,,,,,,:,,,,...:;;1i;:,,,,:;itLCG00000GGCLG0888888888888
|
||||
@@@88888808888888880Ct1;::::::::1GGfi;1C080f:;;:::;i1tLG0880000GLLG0888888888888
|
||||
@@8888888808888888880Cti;::;;;:,,iii1L0GGCfi;:;i;;ii1fC08880000LCGG0008888888888
|
||||
@@@8888888888888888880Cti;:;;,,:1fft1f11fCCL1i;;iiii1fG088800LtCCCG0008888888888
|
||||
@@@88888888@88888888800Cti;iii1ii1ttt;itftti1fff11iitC0880G11CCLGG000088888888@@
|
||||
@@@@@@888@@@@88888888800Ct111itt;:,,,,,,::;itLCCCLi1C088ftCGGLCGC00808888@8@8@@@
|
||||
@@@@@@@@@@@@@@@8888880880GftL1;;;:;1ffft11iii1ttCCLG088G0f1GG00GGG80008888888@@@
|
||||
@@@@@@@@@@@@@@@@888880888800CLfffCGGCGGGCGGCCCCCG00088000LCC08800888800088888888
|
||||
@@@@@@@@@@@@@@@@@@8888G88888888808800000000888888000G0G0G0G088888888880088808888
|
||||
@@@@@@@@@@@@@@@@@@88@880888888081tC088888GCCCGGCGGGGGC0000088888888888888808888@
|
||||
@@@@@@@@@@@@@@@@@@@@88888@88888@800G08888888888888888C00008888888888888888G8888@
|
||||
@@@@@@@@@@@@@@@@@@@@80G8888@@@@@@@@@@8888888888888880800G88@8@@@@@8888888008888@
|
||||
@@@@@@@@@@@@@@@@@@@@@@8880@8@@@@@@@@@8@@@@88888888000888888888@@@88888888G80888@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@8888888@@@@@@@@@@@@@@88@888@G888888888888888888888080888@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@88888@@@@@@@@@@@@@@@@8@888008888888888888888888808888888
|
||||
@@@@@@@@@@@@@@@@@@@@@@@^^^this guy is an idiot^^^^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^no i'm isnt^^^^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
*/
|
494
raw/maps/mp/_stinger.gsc
Normal file
494
raw/maps/mp/_stinger.gsc
Normal file
@ -0,0 +1,494 @@
|
||||
#include maps\mp\_utility;
|
||||
|
||||
InitStingerUsage()
|
||||
{
|
||||
self.stingerStage = undefined;
|
||||
self.stingerTarget = undefined;
|
||||
self.stingerLockStartTime = undefined;
|
||||
self.stingerLostSightlineTime = undefined;
|
||||
|
||||
self thread ResetStingerLockingOnDeath();
|
||||
}
|
||||
|
||||
|
||||
ResetStingerLocking()
|
||||
{
|
||||
if ( !IsDefined( self.stingerUseEntered ) )
|
||||
return;
|
||||
self.stingerUseEntered = undefined;
|
||||
|
||||
self notify( "stop_javelin_locking_feedback" );
|
||||
self notify( "stop_javelin_locked_feedback" );
|
||||
|
||||
self WeaponLockFree();
|
||||
InitStingerUsage();
|
||||
}
|
||||
|
||||
|
||||
ResetStingerLockingOnDeath()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self notify ( "ResetStingerLockingOnDeath" );
|
||||
self endon ( "ResetStingerLockingOnDeath" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill( "death" );
|
||||
self ResetStingerLocking();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StillValidStingerLock( ent )
|
||||
{
|
||||
assert( IsDefined( self ) );
|
||||
|
||||
if ( !IsDefined( ent ) )
|
||||
return false;
|
||||
if ( !(self WorldPointInReticle_Circle( ent.origin, 65, 85 )) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( level.ac130 ) && self.stingerTarget == level.ac130.planeModel && !isDefined( level.ac130player ) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( level.orbitalsupport_planeModel ) && self.stingerTarget == level.orbitalsupport_planeModel && !IsDefined( level.orbitalsupport_player ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
LoopStingerLockingFeedback()
|
||||
{
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "stop_javelin_locking_feedback" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if ( isDefined( level.chopper ) && isDefined( level.chopper.gunner ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.chopper.gunner )
|
||||
level.chopper.gunner playLocalSound( "missile_locking" );
|
||||
|
||||
if ( isDefined( level.ac130player ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.ac130.planeModel )
|
||||
level.ac130player playLocalSound( "missile_locking" );
|
||||
|
||||
self playLocalSound( "stinger_locking" );
|
||||
self PlayRumbleOnEntity( "ac130_25mm_fire" );
|
||||
|
||||
wait 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LoopStingerLockedFeedback()
|
||||
{
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "stop_javelin_locked_feedback" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if ( isDefined( level.chopper ) && isDefined( level.chopper.gunner ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.chopper.gunner )
|
||||
level.chopper.gunner playLocalSound( "missile_locking" );
|
||||
|
||||
if ( isDefined( level.ac130player ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.ac130.planeModel )
|
||||
level.ac130player playLocalSound( "missile_locking" );
|
||||
|
||||
self playLocalSound( "stinger_locked" );
|
||||
self PlayRumbleOnEntity( "ac130_25mm_fire" );
|
||||
|
||||
wait 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
DrawStar( point )
|
||||
{
|
||||
Line( point + (10,0,0), point - (10,0,0) );
|
||||
Line( point + (0,10,0), point - (0,10,0) );
|
||||
Line( point + (0,0,10), point - (0,0,10) );
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
LockSightTest( target )
|
||||
{
|
||||
eyePos = self GetEye();
|
||||
|
||||
if ( !isDefined( target ) ) //targets can disapear during targeting.
|
||||
return false;
|
||||
|
||||
passed = SightTracePassed( eyePos, target.origin, false, target );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
front = target GetPointInBounds( 1, 0, 0 );
|
||||
passed = SightTracePassed( eyePos, front, false, target );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
back = target GetPointInBounds( -1, 0, 0 );
|
||||
passed = SightTracePassed( eyePos, back, false, target );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
StingerDebugDraw( target )
|
||||
{
|
||||
/#
|
||||
if ( GetDVar( "missileDebugDraw" ) != "1" )
|
||||
return;
|
||||
if ( !IsDefined( target ) )
|
||||
return;
|
||||
|
||||
org = target.origin;
|
||||
DrawStar( org );
|
||||
org = target GetPointInBounds( 1, 0, 0 );
|
||||
DrawStar( org );
|
||||
org = target GetPointInBounds( -1, 0, 0 );
|
||||
DrawStar( org );
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
SoftSightTest()
|
||||
{
|
||||
LOST_SIGHT_LIMIT = 500;
|
||||
|
||||
if ( self LockSightTest( self.stingerTarget ) )
|
||||
{
|
||||
self.stingerLostSightlineTime = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( self.stingerLostSightlineTime == 0 )
|
||||
self.stingerLostSightlineTime = getTime();
|
||||
|
||||
timePassed = GetTime() - self.stingerLostSightlineTime;
|
||||
//PrintLn( "Losing sight of target [", timePassed, "]..." );
|
||||
|
||||
if ( timePassed >= LOST_SIGHT_LIMIT )
|
||||
{
|
||||
//PrintLn( "Lost sight of target." );
|
||||
ResetStingerLocking();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GetTargetList()
|
||||
{
|
||||
targets = [];
|
||||
|
||||
if ( inVirtualLobby() )
|
||||
return targets;
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
if ( IsDefined( level.chopper ) && ( level.chopper.team != self.team || ( IsDefined( level.chopper.owner ) && level.chopper.owner == self ) ) )
|
||||
targets[targets.size] = level.chopper;
|
||||
|
||||
if ( IsDefined( level.ac130player ) && level.ac130player.team != self.team )
|
||||
targets[targets.size] = level.ac130.planemodel;
|
||||
|
||||
if ( IsDefined( level.orbitalsupport_player ) && level.orbitalsupport_player.team != self.team )
|
||||
targets[targets.size] = level.orbitalsupport_planemodel;
|
||||
|
||||
if( IsDefined(level.SpawnedWarbirds) )
|
||||
{
|
||||
foreach(warbird in level.SpawnedWarbirds)
|
||||
{
|
||||
if( IsDefined( warbird ) && warbird.team != self.team)
|
||||
targets[targets.size] = warbird;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if(IsDefined(level._orbital_care_pod))
|
||||
{
|
||||
foreach(pod in level._orbital_care_pod)
|
||||
{
|
||||
if(IsDefined(pod.podrocket) && pod.owner.team != self.team)
|
||||
{
|
||||
targets[targets.size] = pod.podrocket;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if ( IsDefined( level.harriers) )
|
||||
{
|
||||
foreach( harrier in level.harriers )
|
||||
{
|
||||
if ( IsDefined( harrier ) && ( harrier.team != self.team || ( IsDefined( harrier.owner ) && harrier.owner == self ) ) )
|
||||
targets[targets.size] = harrier;
|
||||
}
|
||||
}
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
//for all teams
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
//that are not our team
|
||||
if( self.team != level.teamNameList[i] )
|
||||
{
|
||||
//does that team have any uav's
|
||||
if( level.UAVModels[level.teamNameList[i]].size )
|
||||
{
|
||||
//add each uav to the target list
|
||||
foreach ( UAV in level.UAVModels[level.teamNameList[i]] )
|
||||
{
|
||||
targets[targets.size] = UAV;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( level.UAVModels[level.otherTeam[self.team]].size )
|
||||
{
|
||||
foreach ( UAV in level.UAVModels[level.otherTeam[self.team]] )
|
||||
targets[targets.size] = UAV;
|
||||
}
|
||||
|
||||
if ( IsDefined( level.littleBirds ) )
|
||||
{
|
||||
foreach ( lb in level.littleBirds )
|
||||
{
|
||||
if ( IsDefined( lb ) && ( lb.team != self.team || ( isDefined( lb.owner ) && lb.owner == self ) ) )
|
||||
targets[targets.size] = lb;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( level.ugvs ) )
|
||||
{
|
||||
foreach ( ugv in level.ugvs )
|
||||
{
|
||||
if ( IsDefined( ugv ) && ( ugv.team != self.team || ( isDefined( ugv.owner ) && ugv.owner == self ) ) )
|
||||
targets[targets.size] = ugv;
|
||||
}
|
||||
}
|
||||
|
||||
//This exposes missile tracking issues - CUT IW5 Jordan
|
||||
/*
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !isAlive( player ) )
|
||||
continue;
|
||||
|
||||
if ( level.teamBased && player.team == self.team )
|
||||
continue;
|
||||
|
||||
if ( player == self )
|
||||
continue;
|
||||
|
||||
if ( player isJuggernaut() )
|
||||
targets[targets.size] = player;
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( level.chopper ) ) //check for teams IW5: ( level.chopper.owner != self )
|
||||
targets[targets.size] = level.chopper;
|
||||
|
||||
if ( isDefined( level.ac130player ) )
|
||||
targets[targets.size] = level.ac130.planemodel;
|
||||
|
||||
if ( isDefined( level.harriers) )
|
||||
{
|
||||
foreach( harrier in level.harriers )
|
||||
{
|
||||
if ( isDefined( harrier ) )
|
||||
targets[targets.size] = harrier;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if(IsDefined(level._orbital_care_pod))
|
||||
{
|
||||
foreach(pod in level._orbital_care_pod)
|
||||
{
|
||||
if(IsDefined(pod.podrocket))
|
||||
{
|
||||
targets[targets.size] = pod.podrocket;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if ( level.UAVModels.size )
|
||||
{
|
||||
foreach ( ownerGuid, UAV in level.UAVModels )
|
||||
{
|
||||
if ( isDefined( UAV.owner ) && UAV.owner == self )
|
||||
continue;
|
||||
|
||||
targets[targets.size] = UAV;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDefined( level.littleBirds ) )
|
||||
{
|
||||
foreach ( lb in level.littleBirds )
|
||||
{
|
||||
if ( !isDefined( lb ) )
|
||||
continue;
|
||||
|
||||
targets[targets.size] = lb;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( level.ugvs ) )
|
||||
{
|
||||
foreach ( ugv in level.ugvs )
|
||||
{
|
||||
if ( !IsDefined( ugv ) )
|
||||
continue;
|
||||
|
||||
targets[targets.size] = ugv;
|
||||
}
|
||||
}
|
||||
|
||||
// foreach ( player in level.players )
|
||||
// {
|
||||
// if ( !isAlive( player ) )
|
||||
// continue;
|
||||
//
|
||||
// if ( level.teamBased && player.team == self.team )
|
||||
// continue;
|
||||
//
|
||||
// if ( player == self )
|
||||
// continue;
|
||||
//
|
||||
// targets[targets.size] = player;
|
||||
// }
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
StingerUsageLoop()
|
||||
{
|
||||
if ( !IsPlayer(self) )
|
||||
return;
|
||||
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
self endon("faux_spawn");
|
||||
|
||||
LOCK_LENGTH = 1000;
|
||||
|
||||
InitStingerUsage();
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait 0.05;
|
||||
|
||||
if ( self PlayerADS() < 0.95 )
|
||||
{
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
weapon = self getCurrentWeapon();
|
||||
|
||||
// Do not run any stinger logic if stingerM7 - stingerM7 has its own script in _stingerm7.gsc.
|
||||
if ( IsSubStr( weapon, "stingerm7" ) )
|
||||
continue;
|
||||
|
||||
if ( weapon != "stinger_mp" && weapon != "iw5_maaws_mp" )
|
||||
{
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
self.stingerUseEntered = true;
|
||||
|
||||
if ( !IsDefined( self.stingerStage ) )
|
||||
self.stingerStage = 0;
|
||||
|
||||
StingerDebugDraw( self.stingerTarget );
|
||||
|
||||
if ( self.stingerStage == 0 ) // searching for target
|
||||
{
|
||||
targets = GetTargetList();
|
||||
if ( targets.size == 0 )
|
||||
continue;
|
||||
|
||||
targetsInReticle = [];
|
||||
foreach ( target in targets )
|
||||
{
|
||||
if ( !isDefined( target ) )
|
||||
continue;
|
||||
|
||||
insideReticle = self WorldPointInReticle_Circle( target.origin, 65, 75 );
|
||||
|
||||
if ( insideReticle )
|
||||
targetsInReticle[targetsInReticle.size] = target;
|
||||
}
|
||||
if ( targetsInReticle.size == 0 )
|
||||
continue;
|
||||
|
||||
sortedTargets = SortByDistance( targetsInReticle, self.origin );
|
||||
if ( !( self LockSightTest( sortedTargets[0] ) ) )
|
||||
continue;
|
||||
|
||||
//PrintLn( "Found a target to lock to..." );
|
||||
thread LoopStingerLockingFeedback();
|
||||
self.stingerTarget = sortedTargets[0];
|
||||
self.stingerLockStartTime = GetTime();
|
||||
self.stingerStage = 1;
|
||||
self.stingerLostSightlineTime = 0;
|
||||
}
|
||||
|
||||
if ( self.stingerStage == 1 ) // locking on to a target
|
||||
{
|
||||
if ( !(self StillValidStingerLock( self.stingerTarget )) )
|
||||
{
|
||||
//PrintLn( "Failed to get lock." );
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
passed = SoftSightTest();
|
||||
if ( !passed )
|
||||
continue;
|
||||
|
||||
timePassed = getTime() - self.stingerLockStartTime;
|
||||
//PrintLn( "Locking [", timePassed, "]..." );
|
||||
if( self _hasPerk( "specialty_fasterlockon" ) )
|
||||
{
|
||||
if( timePassed < ( LOCK_LENGTH * 0.5 ) )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( timePassed < LOCK_LENGTH )
|
||||
continue;
|
||||
}
|
||||
|
||||
self notify( "stop_javelin_locking_feedback" );
|
||||
thread LoopStingerLockedFeedback();
|
||||
|
||||
//PrintLn( "Locked!");
|
||||
self WeaponLockFinalize( self.stingerTarget );
|
||||
|
||||
self.stingerStage = 2;
|
||||
}
|
||||
|
||||
if ( self.stingerStage == 2 ) // target locked
|
||||
{
|
||||
passed = SoftSightTest();
|
||||
if ( !passed )
|
||||
continue;
|
||||
|
||||
if ( !(self StillValidStingerLock( self.stingerTarget )) )
|
||||
{
|
||||
//PrintLn( "Gave up lock." );
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
472
raw/maps/mp/_stingerm7.gsc
Normal file
472
raw/maps/mp/_stingerm7.gsc
Normal file
@ -0,0 +1,472 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
CONST_stinger_weaponname = "stingerm7";
|
||||
|
||||
CONST_lock_require_los = true;
|
||||
CONST_lock_angle = 5;
|
||||
CONST_lock_time = 1;
|
||||
CONST_max_locks = 4;
|
||||
CONST_nosight_time_limit = 500;
|
||||
|
||||
|
||||
stingerm7_think()
|
||||
{
|
||||
self thread stingerm7_targeting();
|
||||
self thread stingerm7_monitor_fire();
|
||||
}
|
||||
|
||||
stingerm7_targeting()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
|
||||
self.stingerm7_info = SpawnStruct();
|
||||
self.stingerm7_info.locked_targets = [];
|
||||
self.stingerm7_info.locking_time = 0;
|
||||
was_ads_stingerm7 = false;
|
||||
|
||||
while( true )
|
||||
{
|
||||
if ( IsSubStr( self GetCurrentWeapon(), CONST_stinger_weaponname ) && self PlayerAds() > .99 )
|
||||
{
|
||||
was_ads_stingerm7 = true;
|
||||
|
||||
if ( self.stingerm7_info.locked_targets.size > 0 )
|
||||
{
|
||||
self remove_invalid_locks();
|
||||
}
|
||||
self.stingerm7_info.locked_targets = array_remove_dead( array_removeUndefined( self.stingerm7_info.locked_targets ) );
|
||||
|
||||
if ( IsDefined( self.stingerm7_info.locking_target ) )
|
||||
{
|
||||
// Check that the stinger's current locking target is still valid. If not, remove it.
|
||||
if ( !locking_target_still_valid( self.stingerm7_info.locking_target ) )
|
||||
{
|
||||
//Target_Remove(self.stingerm7_info.locking_target);
|
||||
self.stingerm7_info.locking_target = undefined;
|
||||
self notify( "stop_javelin_locking_feedback" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( self.stingerm7_info.locking_target ) )
|
||||
{
|
||||
// If the stinger locking target wasn't removed above, increment the locking_time.
|
||||
self.stingerm7_info.locking_time += .05;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is no locking_target, try to get a new one.
|
||||
self.stingerm7_info.locking_time = 0;
|
||||
|
||||
if ( self.stingerm7_info.locked_targets.size < CONST_max_locks )
|
||||
{
|
||||
self.stingerm7_info.locking_target = self get_best_locking_target();
|
||||
if ( IsDefined( self.stingerm7_info.locking_target ) )
|
||||
{
|
||||
// Acquired a new locking target.
|
||||
self thread locking_feedback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( self.stingerm7_info.locking_time >= CONST_lock_time && IsDefined( self.stingerm7_info.locking_target ) && self.stingerm7_info.locked_targets.size < CONST_max_locks )
|
||||
{
|
||||
// Lock on acquired.
|
||||
self notify( "stop_javelin_locking_feedback" );
|
||||
self.stingerm7_info.locked_targets[ self.stingerm7_info.locked_targets.size ] = self.stingerm7_info.locking_target;
|
||||
|
||||
self thread locked_feedback();
|
||||
|
||||
self.stingerm7_info.locking_target = undefined;
|
||||
}
|
||||
|
||||
if ( self.stingerm7_info.locked_targets.size > 0 )
|
||||
{
|
||||
self WeaponLockFinalize( self.stingerm7_info.locked_targets[ 0 ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
self WeaponLockFree();
|
||||
self notify( "stop_javelin_locked_feedback" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( was_ads_stingerm7 == true )
|
||||
{
|
||||
// The player is not ADS with the StingerM7.
|
||||
was_ads_stingerm7 = false;
|
||||
self WeaponLockFree();
|
||||
self notify( "stop_javelin_locking_feedback" );
|
||||
self notify( "stop_javelin_locked_feedback" );
|
||||
|
||||
self.stingerm7_info.locked_targets = [];
|
||||
|
||||
if ( IsDefined( self.stingerm7_info.locking_target ) )
|
||||
{
|
||||
//Target_Remove(self.stingerm7_info.locking_target);
|
||||
self.stingerm7_info.locking_target = undefined;
|
||||
}
|
||||
|
||||
self.stingerm7_info.locking_time = 0;
|
||||
}
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
stingerm7_monitor_fire()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "missile_fire", projectile, weaponName );
|
||||
if ( IsSubStr( weaponName, CONST_stinger_weaponname ) )
|
||||
{
|
||||
thread stinger_fire( self, projectile, weaponName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stinger_fire( player, projectile, weapon_name )
|
||||
{
|
||||
// A bit of hack, we could instead modify the engine to fire multiple projectiles.
|
||||
// instead, we just delete the one it fired and make our own.
|
||||
|
||||
init_origin = (0,0,0);
|
||||
init_angles = (0,0,0);
|
||||
if ( isDefined( projectile ) )
|
||||
{
|
||||
init_origin = projectile.origin;
|
||||
init_angles = projectile.angles;
|
||||
projectile Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
player.stingerm7_info.locked_targets = array_remove_dead( array_removeUndefined( self.stingerm7_info.locked_targets ) );
|
||||
|
||||
missiles = [];
|
||||
|
||||
for ( i = 0; i < CONST_max_locks; i++ )
|
||||
{
|
||||
fire_angles = init_angles + random_vector( 20, 20, 20 );
|
||||
fire_direction = AnglesToForward( fire_angles );
|
||||
|
||||
new_projectile = MagicBullet( weapon_name, init_origin, init_origin + fire_direction, player );
|
||||
new_projectile.owner = player;
|
||||
|
||||
// every locked target gets at least one shot, and remaining shots are randomly divided amongst locked targets
|
||||
if ( player.stingerm7_info.locked_targets.size > 0 )
|
||||
{
|
||||
target = undefined;
|
||||
if ( i < player.stingerm7_info.locked_targets.size )
|
||||
target = player.stingerm7_info.locked_targets[ i ];
|
||||
else
|
||||
target = player.stingerm7_info.locked_targets[ RandomInt( player.stingerm7_info.locked_targets.size ) ];
|
||||
|
||||
new_projectile Missile_SetTargetEnt( target, stingerm7_get_target_offset( target ) );
|
||||
new_projectile.lockedStingerTarget = target;
|
||||
}
|
||||
|
||||
missiles[ missiles.size ] = new_projectile;
|
||||
}
|
||||
|
||||
level notify( "stinger_fired", player, missiles );
|
||||
|
||||
// you used all your ammo
|
||||
player SetWeaponAmmoClip( weapon_name, 0 );
|
||||
}
|
||||
|
||||
anyStingerMissileLockedOn( missiles, target )
|
||||
{
|
||||
foreach ( missile in missiles )
|
||||
{
|
||||
if ( IsDefined( missile.lockedStingerTarget ) && missile.lockedStingerTarget == target )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get_best_locking_target()
|
||||
{
|
||||
Assert( IsPlayer( self ) );
|
||||
|
||||
enemy_team = getOtherTeam( self.team );
|
||||
enemy_players = [];
|
||||
|
||||
//add enemy players
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( level.teamBased && player.team == self.team )
|
||||
continue;
|
||||
|
||||
if( !isReallyAlive( player) )
|
||||
continue;
|
||||
|
||||
enemy_players[ enemy_players.size ] = player;
|
||||
}
|
||||
|
||||
//add enemy vehicles
|
||||
all_vehicles = Vehicle_GetArray();
|
||||
enemy_vehicles = [];
|
||||
|
||||
foreach( vehicle in all_vehicles )
|
||||
{
|
||||
if ( !isDefined( vehicle.owner ) )
|
||||
continue;
|
||||
|
||||
if ( vehicle maps\mp\killstreaks\_aerial_utility::vehicleIsCloaked() )
|
||||
continue;
|
||||
|
||||
if ( level.teamBased && vehicle.owner.team == self.team )
|
||||
continue;
|
||||
|
||||
enemy_vehicles[ enemy_vehicles.size ] = vehicle;
|
||||
}
|
||||
|
||||
//add horde mode targets
|
||||
if( IsDefined( level.isHorde ) && level.isHorde )
|
||||
{
|
||||
foreach( agent in level.agentarray )
|
||||
{
|
||||
if ( level.teamBased && agent.team == self.team )
|
||||
continue;
|
||||
|
||||
if( !isReallyAlive( agent) )
|
||||
continue;
|
||||
|
||||
enemy_players[ enemy_players.size ] = agent;
|
||||
}
|
||||
|
||||
foreach( vehicle in all_vehicles )
|
||||
{
|
||||
|
||||
if ( vehicle maps\mp\killstreaks\_aerial_utility::vehicleIsCloaked() )
|
||||
continue;
|
||||
|
||||
if ( level.teamBased && vehicle.team == self.team )
|
||||
continue;
|
||||
|
||||
enemy_vehicles[ enemy_vehicles.size ] = vehicle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
aerialTargets = maps\mp\killstreaks\_killstreaks::getAerialKillstreakArray( enemy_team );
|
||||
|
||||
targets = array_combine( enemy_players, enemy_vehicles );
|
||||
targets = array_combine( targets, aerialTargets );
|
||||
if(IsDefined(level.stingerLockOnEntsFunc))
|
||||
targets = array_combine( targets, [[level.stingerLockOnEntsFunc]](self) );
|
||||
|
||||
eye_origin = self GetEye();
|
||||
eye_dir = AnglesToForward( self GetPlayerAngles() );
|
||||
|
||||
best_target = undefined;
|
||||
best_target_dot = Cos( CONST_lock_angle );
|
||||
foreach( target in targets )
|
||||
{
|
||||
if ( !array_contains( self.stingerm7_info.locked_targets, target ) )
|
||||
{
|
||||
target_origin = stingerm7_get_target_pos( target );
|
||||
dot = VectorDot( VectorNormalize( target_origin - eye_origin ), eye_dir );
|
||||
if ( dot > best_target_dot )
|
||||
{
|
||||
result = undefined;
|
||||
can_target = !CONST_lock_require_los;
|
||||
if ( !can_target )
|
||||
{
|
||||
tracePassed = BulletTracePassed( eye_origin, target_origin, false, target );
|
||||
if ( tracePassed )
|
||||
can_target = true;
|
||||
}
|
||||
|
||||
if ( can_target )
|
||||
{
|
||||
best_target = target;
|
||||
best_target_dot = dot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_target;
|
||||
}
|
||||
|
||||
locking_target_still_valid( target )
|
||||
{
|
||||
Assert( IsPlayer( self ) );
|
||||
|
||||
eye_origin = self GetEye();
|
||||
eye_dir = AnglesToForward( self GetPlayerAngles() );
|
||||
target_origin = stingerm7_get_target_pos( target );
|
||||
|
||||
if ( ( IsPlayer( target ) || IsBot( target ) || ( isDefined( level.isHorde ) && level.isHorde && IsAgent( target )) ) && !isReallyAlive( target ) )
|
||||
return false;
|
||||
|
||||
if ( VectorDot( VectorNormalize( target_origin - eye_origin ), eye_dir ) > Cos( CONST_lock_angle ) )
|
||||
{
|
||||
if (!CONST_lock_require_los || BulletTracePassed( eye_origin, target_origin, false, target ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
remove_invalid_locks()
|
||||
{
|
||||
for ( i = 0; i <= self.stingerm7_info.locked_targets.size; i++ )
|
||||
{
|
||||
if ( IsDefined( self.stingerm7_info.locked_targets[i] ) && IsDefined( self.stingerm7_info.locked_targets[i].origin ) )
|
||||
{
|
||||
if ( !IsDefined( self.stingerm7_info.locked_targets[i].sight_lost_time ) )
|
||||
{
|
||||
self.stingerm7_info.locked_targets[i].sight_lost_time = -1;
|
||||
}
|
||||
|
||||
origin_mod = ( 0, 0, 0 );
|
||||
if ( IsPlayer( self.stingerm7_info.locked_targets[i] ) || IsBot( self.stingerm7_info.locked_targets[i] ) )
|
||||
{
|
||||
origin_mod = ( 0, 0, 64 );
|
||||
}
|
||||
|
||||
if ( self WorldPointInReticle_Rect( self.stingerm7_info.locked_targets[i].origin + origin_mod, 50, 400, 200 ) )
|
||||
{
|
||||
if ( BulletTracePassed( self GetEye(), self.stingerm7_info.locked_targets[i].origin + origin_mod, false, self.stingerm7_info.locked_targets[i] ) )
|
||||
{
|
||||
// All checks pass, the target is still valid. Move onto the next one in the list.
|
||||
self.stingerm7_info.locked_targets[i].sight_lost_time = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// A check failed, line of sight to target lost.
|
||||
if ( self.stingerm7_info.locked_targets[i].sight_lost_time == -1 )
|
||||
{
|
||||
self.stingerm7_info.locked_targets[i].sight_lost_time = GetTime();
|
||||
}
|
||||
else if ( GetTime() - self.stingerm7_info.locked_targets[i].sight_lost_time >= CONST_nosight_time_limit )
|
||||
{
|
||||
self.stingerm7_info.locked_targets[i].sight_lost_time = -1;
|
||||
self.stingerm7_info.locked_targets[i] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stingerm7_get_target_pos( target )
|
||||
{
|
||||
if(IsDefined(target.getStingerTargetPosFunc))
|
||||
return target [[target.getStingerTargetPosFunc]]();
|
||||
|
||||
return target GetPointInBounds( 0, 0, 0 );
|
||||
}
|
||||
|
||||
stingerm7_get_target_offset( target )
|
||||
{
|
||||
return stingerm7_get_target_pos( target ) - target.origin;
|
||||
}
|
||||
|
||||
locking_feedback()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "stop_javelin_locking_feedback" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if( isDefined( level.SpawnedWarbirds ) )
|
||||
{
|
||||
foreach( warbird in level.SpawnedWarbirds )
|
||||
{
|
||||
if( isDefined( warbird.owner ) && isDefined( warbird.player ) && isDefined( self.stingerm7_info.locking_target ) && self.stingerm7_info.locking_target == warbird )
|
||||
warbird.owner playLocalSound( "wpn_stingerm7_enemy_locked" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDefined( level.orbitalsupport_player ) && isDefined( self.stingerm7_info.locking_target ) && self.stingerm7_info.locking_target == level.orbitalsupport_planeModel )
|
||||
level.orbitalsupport_player playLocalSound( "wpn_stingerm7_enemy_locked" );
|
||||
|
||||
self playLocalSound( "wpn_stingerm7_locking" );
|
||||
self PlayRumbleOnEntity( "heavygun_fire" );
|
||||
|
||||
wait 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
locked_feedback()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "stop_javelin_locked_feedback" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if( isDefined( level.SpawnedWarbirds ) )
|
||||
{
|
||||
foreach( warbird in level.SpawnedWarbirds )
|
||||
{
|
||||
if( isDefined( warbird.owner ) && isDefined( warbird.player ) && isDefined( self.stingerm7_info.locked_targets ) && IsInArray( self.stingerm7_info.locked_targets, warbird ) )
|
||||
warbird.owner playLocalSound( "wpn_stingerm7_enemy_locked" );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDefined( level.orbitalsupport_player ) && isDefined( self.stingerm7_info.locked_targets ) && IsInArray( self.stingerm7_info.locked_targets, level.orbitalsupport_planeModel ) )
|
||||
level.orbitalsupport_player playLocalSound( "wpn_stingerm7_enemy_locked" );
|
||||
|
||||
self playLocalSound( "wpn_stingerm7_locked" );
|
||||
self PlayRumbleOnEntity( "heavygun_fire" );
|
||||
|
||||
wait 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
|
||||
array_remove_dead( array )
|
||||
{
|
||||
temp = [];
|
||||
|
||||
foreach( thing in array )
|
||||
{
|
||||
if ( !isAlive( thing ) )
|
||||
continue;
|
||||
|
||||
temp[ temp.size ] = thing;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
random_vector( num1, num2, num3 )
|
||||
{
|
||||
return( RandomFloat( num1 ) - num1 * 0.5, RandomFloat( num2 ) - num2 * 0.5, RandomFloat( num3 ) - num3 * 0.5 );
|
||||
}
|
||||
|
||||
IsInArray( array, Ent )
|
||||
{
|
||||
if ( IsDefined( array ) )
|
||||
{
|
||||
foreach ( Index in array )
|
||||
{
|
||||
if ( Index == Ent )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
46
raw/maps/mp/_stock.gsc
Normal file
46
raw/maps/mp/_stock.gsc
Normal file
@ -0,0 +1,46 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
stock_think()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
has_stock = false;
|
||||
|
||||
attachment_list = GetWeaponAttachments( self GetCurrentWeapon() );
|
||||
|
||||
if( isDefined( attachment_list ) )
|
||||
{
|
||||
foreach( attachment in attachment_list )
|
||||
{
|
||||
if( attachment == "stock" )
|
||||
{
|
||||
has_stock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !has_stock )
|
||||
{
|
||||
if( self HasPerk( "specialty_stalker", true ) )
|
||||
{
|
||||
self unsetPerk( "specialty_stalker", true );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !self HasPerk( "specialty_stalker", true ) )
|
||||
{
|
||||
self setPerk( "specialty_stalker", true, false );
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
60
raw/maps/mp/_target_enhancer.gsc
Normal file
60
raw/maps/mp/_target_enhancer.gsc
Normal file
@ -0,0 +1,60 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
target_enhancer_think()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
angle = 10;
|
||||
cos_angle = Cos( angle );
|
||||
ads_frac = 0.5;
|
||||
|
||||
while( true )
|
||||
{
|
||||
has_target_enhancer = false;
|
||||
|
||||
attachment_list = GetWeaponAttachments( self GetCurrentWeapon() );
|
||||
|
||||
if( isDefined( attachment_list ) )
|
||||
{
|
||||
foreach( attachment in attachment_list )
|
||||
{
|
||||
if( attachment == "opticstargetenhancer" )
|
||||
{
|
||||
has_target_enhancer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while( has_target_enhancer && self PlayerAds() < ads_frac )
|
||||
{
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
if( !has_target_enhancer )
|
||||
{
|
||||
wait( 0.05 );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( self IsUsingTurret() )
|
||||
{
|
||||
wait( 0.05 );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( IsDefined( self.empOn ) && self.empOn )
|
||||
{
|
||||
waitframe();
|
||||
continue;
|
||||
}
|
||||
|
||||
childthread maps\mp\_threatdetection::detection_highlight_hud_effect( self, 0.05, true );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
1457
raw/maps/mp/_teleport.gsc
Normal file
1457
raw/maps/mp/_teleport.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1022
raw/maps/mp/_threatdetection.gsc
Normal file
1022
raw/maps/mp/_threatdetection.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1539
raw/maps/mp/_tracking_drone.gsc
Normal file
1539
raw/maps/mp/_tracking_drone.gsc
Normal file
File diff suppressed because it is too large
Load Diff
128
raw/maps/mp/_trackrounds.gsc
Normal file
128
raw/maps/mp/_trackrounds.gsc
Normal file
@ -0,0 +1,128 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
//==============================================================================
|
||||
trackrounds_think() // self = player
|
||||
//==============================================================================
|
||||
{
|
||||
if( GetDvar( "mapname" ) == getdvar( "virtualLobbyMap") )
|
||||
return;
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
self.trackrounds = SpawnStruct();
|
||||
self.trackrounds.has_paint_pro = false;
|
||||
self.trackrounds.has_trackrounds = false;
|
||||
|
||||
if( self _hasPerk( "specialty_paint_pro" ) )
|
||||
self.trackrounds.has_paint_pro = true;
|
||||
|
||||
weapon = self GetCurrentWeapon();
|
||||
self toggle_has_trackrounds( weapon );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "weapon_change", weapon );
|
||||
|
||||
/* Sometimes the notify will return "none" due to the weapon changing. Waiting for a split
|
||||
second and trying again seems to catch these situations. */
|
||||
if( weapon == "none" )
|
||||
{
|
||||
wait 0.4;
|
||||
weapon = self GetCurrentWeapon();
|
||||
if( weapon == "none" )
|
||||
return;
|
||||
}
|
||||
|
||||
self toggle_has_trackrounds( weapon );
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
toggle_has_trackrounds( weapon ) // self == player "attacker"
|
||||
//==============================================================================
|
||||
{
|
||||
attachments = undefined;
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
attachments = GetWeaponAttachments( weapon );
|
||||
|
||||
if( isdefined( attachments ) )
|
||||
{
|
||||
foreach ( attachment in attachments )
|
||||
{
|
||||
if( attachment == "trackrounds" )
|
||||
{
|
||||
// self IPrintLnBold( "you have a tracker" );
|
||||
self.trackrounds.has_trackrounds = true;
|
||||
self givePerk( "specialty_paint_pro", false, false );
|
||||
return;
|
||||
}
|
||||
}
|
||||
// None of the attachments were trackrounds
|
||||
// self IPrintLnBold( "No tracker" );
|
||||
self.trackrounds.has_trackrounds = false;
|
||||
|
||||
// Only remove specialty_paint_pro if they did not already have it from engineer perk stack.
|
||||
if( !self.trackrounds.has_paint_pro )
|
||||
self _unsetPerk( "specialty_paint_pro" );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
set_painted_trackrounds( attacker ) // self == victim
|
||||
//==============================================================================
|
||||
{
|
||||
// this is called from cac_modified_damage
|
||||
if( IsPlayer( self ) )
|
||||
{
|
||||
if ( IsDefined( self.painted_tracked ) && self.painted_tracked )
|
||||
return;
|
||||
|
||||
self.painted_tracked = true;
|
||||
|
||||
self thread trackrounds_mark_till_death();
|
||||
}
|
||||
}
|
||||
|
||||
trackrounds_death()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self.painted_tracked = false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
trackrounds_mark_till_death() // self == victim
|
||||
//==============================================================================
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self thread trackrounds_death();
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait 0.1;
|
||||
|
||||
if( self HasPerk( "specialty_radararrow", true ) )
|
||||
continue;
|
||||
|
||||
if( self HasPerk( "specialty_radarblip", true ) )
|
||||
continue;
|
||||
|
||||
self SetPerk( "specialty_radarblip", true, false );
|
||||
}
|
||||
}
|
716
raw/maps/mp/_tridrone.gsc
Normal file
716
raw/maps/mp/_tridrone.gsc
Normal file
@ -0,0 +1,716 @@
|
||||
/*
|
||||
Tri Drone
|
||||
Author: Keith Evans
|
||||
Description: Three proximity based mines with a small detection radius causing small damage hit and short flashbang fx
|
||||
can be stacked to increase effectiveness
|
||||
*/
|
||||
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
#include maps\mp\perks\_perkfunctions;
|
||||
|
||||
CONST_Mine_TriggerDelay_Perk = 3;
|
||||
CONST_Mine_TriggerDelay = 1;
|
||||
CONST_Mine_TimOut = 120;
|
||||
|
||||
CONST_Mine_TriggerRadius = 192;
|
||||
CONST_Mine_TriggerHeight = 192;
|
||||
CONST_Mine_DamageRadius = 192;
|
||||
CONST_Mine_DamageMax = 60;
|
||||
CONST_Mine_DamageMin = 60;
|
||||
|
||||
CONST_Mine_ClipSize = 3;
|
||||
|
||||
watchTriDroneUsage()
|
||||
{
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
triDroneAmmoInit();
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "grenade_fire", grenade, weapname );
|
||||
if ( weapname == "tri_drone_mp")
|
||||
{
|
||||
if( !IsAlive( self ) )
|
||||
{
|
||||
grenade delete();
|
||||
return;
|
||||
}
|
||||
|
||||
thread tryUseTriDrone( grenade );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
PreCacheShellShock( "flashbang_mp" );
|
||||
precacheModel( "projectile_bouncing_betty_grenade_small" );
|
||||
precacheModel( "projectile_bouncing_betty_grenade_small_bombsquad" );
|
||||
|
||||
level.triDroneSettings = SpawnStruct();
|
||||
level.triDroneSettings.mineCountdown = 1;
|
||||
level.triDroneSettings.blastRadius = 132;
|
||||
level.triDroneSettings.droneBounceHeight = 128;
|
||||
level.triDroneSettings.droneMesh = "projectile_bouncing_betty_grenade_small";
|
||||
level.triDroneSettings.droneBombSquadMesh = "projectile_bouncing_betty_grenade_small_bombsquad";
|
||||
level.triDroneSettings.droneExplosionFx = LoadFX( "vfx/explosion/frag_grenade_default" );
|
||||
level.triDroneSettings.beacon[ "enemy" ] = loadfx( "vfx/lights/light_c4_blink" );
|
||||
level.triDroneSettings.beacon[ "friendly" ] = loadfx( "vfx/lights/light_mine_blink_friendly" );
|
||||
level.triDroneSettings.dome = LoadFX( "vfx/unique/orbital_dome_ground_friendly" );
|
||||
}
|
||||
|
||||
triDroneAmmoInit()
|
||||
{
|
||||
if(!IsDefined(self.triDroneAmmo))
|
||||
{
|
||||
self.triDroneAmmo = 0;
|
||||
self thread showAmmoCount();
|
||||
}
|
||||
|
||||
self.triDroneAmmo = 0;
|
||||
self.triDroneAmmo+=CONST_Mine_ClipSize;
|
||||
|
||||
if(!IsDefined(self.triDroneDeployed))
|
||||
self.triDroneDeployed = [];
|
||||
}
|
||||
|
||||
tryUseTriDrone( grenade ) // self == player
|
||||
{
|
||||
// Force Init for repeated debug use )
|
||||
if(self.triDroneAmmo<=0)
|
||||
{
|
||||
triDroneAmmoInit();
|
||||
}
|
||||
|
||||
if(self.triDroneAmmo>0)
|
||||
{
|
||||
thread LaunchTriDrone( grenade );
|
||||
self.triDroneAmmo--;
|
||||
|
||||
if(self.triDroneAmmo>=1)
|
||||
self GiveWeapon("tri_drone_mp");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LaunchTriDrone( grenade )
|
||||
{
|
||||
Mine = self SpawnMine( grenade ) ;
|
||||
|
||||
self.triDroneDeployed =array_add(self.triDroneDeployed, Mine);
|
||||
|
||||
thread MonitorPlayerDeath( Mine );
|
||||
}
|
||||
|
||||
ActivateGroupedTriDrones( Player )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
foreach(triDrone in Player.triDroneDeployed)
|
||||
{
|
||||
if(IsDefined(triDrone))
|
||||
{
|
||||
if(triDrone != self)
|
||||
{
|
||||
triDrone.trigger notify("trigger");
|
||||
wait .25;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RemoveGroupedTriDrone( )
|
||||
{
|
||||
self.owner.triDroneDeployed = array_remove(self.owner.triDroneDeployed, self);
|
||||
}
|
||||
|
||||
MonitorPlayerDeath( Mine )
|
||||
{
|
||||
Mine endon ("death");
|
||||
self waittill( "death" );
|
||||
|
||||
self.triDroneAmmo = 0;
|
||||
|
||||
if(IsDefined(Mine.pickuptrigger))
|
||||
Mine.pickuptrigger delete();
|
||||
|
||||
Mine delete();
|
||||
}
|
||||
|
||||
|
||||
////=================================================================================================================//
|
||||
//// TRI DRONE MECHANIC //
|
||||
////=================================================================================================================//
|
||||
|
||||
SpawnMine( grenade )
|
||||
{
|
||||
|
||||
grenade waittill( "missile_stuck" );
|
||||
|
||||
Assert( isDefined( self ) );
|
||||
|
||||
tracedown = bulletTrace( grenade.origin, grenade.origin - (0, 0, 4), false, grenade );
|
||||
|
||||
traceup = bulletTrace( grenade.origin, grenade.origin + (0, 0, 4), false, grenade );
|
||||
|
||||
ForwardAngles = AnglesToForward(grenade.angles);
|
||||
|
||||
traceforward = bulletTrace( grenade.origin + (0, 0, 4), (grenade.origin + (ForwardAngles*4)), false, grenade );
|
||||
|
||||
trace = undefined;
|
||||
IsUp = false;
|
||||
IsForward = false;
|
||||
|
||||
if(traceforward["surfacetype"] != "none")
|
||||
{
|
||||
trace = traceforward;
|
||||
IsForward = true;
|
||||
}
|
||||
else if(traceup["surfacetype"] != "none")
|
||||
{
|
||||
trace = traceup;
|
||||
IsUp = true;
|
||||
}
|
||||
else if(tracedown["surfacetype"] != "none")
|
||||
trace = tracedown;
|
||||
else
|
||||
trace = tracedown; //fallback
|
||||
|
||||
SpawnOrigin = trace[ "position" ];
|
||||
|
||||
if(SpawnOrigin == traceup[ "position" ] )
|
||||
{
|
||||
SpawnOrigin+=(0,0,-5);
|
||||
}
|
||||
|
||||
TriDrone = Spawn( "script_model", SpawnOrigin );
|
||||
|
||||
TriDrone.IsUp = IsUp;
|
||||
TriDrone.IsForward = IsForward;
|
||||
|
||||
normal = vectornormalize( trace["normal"] );
|
||||
angles = vectortoangles( normal );
|
||||
|
||||
angles += ( 90, 0, 0 );
|
||||
|
||||
TriDrone.angles = angles;
|
||||
|
||||
TriDrone SetModel( level.triDroneSettings.droneMesh );
|
||||
TriDrone.owner = self;
|
||||
TriDrone SetOtherEnt(self);
|
||||
TriDrone.killCamOffset = ( 0, 0, 55 );
|
||||
TriDrone.killCamEnt = Spawn( "script_model", TriDrone.origin + TriDrone.killCamOffset );
|
||||
TriDrone.stunned = false;
|
||||
|
||||
TriDrone.weaponname = "tri_drone_mp";
|
||||
|
||||
grenade delete();
|
||||
|
||||
//ProximityMine thread showDebugRadius();
|
||||
|
||||
level.mines[level.mines.size] = TriDrone;
|
||||
|
||||
TriDrone thread createBombSquadModel( level.triDroneSettings.droneBombSquadMesh, "tag_origin", self );
|
||||
TriDrone thread mineBeacon();
|
||||
TriDrone thread setTriDroneTeamHeadIcon( self.team );
|
||||
TriDrone thread mineDamageMonitor();
|
||||
TriDrone thread mineProximityTrigger( self );
|
||||
TriDrone thread mineSelfDestruct();
|
||||
TriDrone thread deleteMineOnTeamSwitch( self );
|
||||
TriDrone thread handleEMP( self, "apm_mine" );
|
||||
|
||||
return TriDrone;
|
||||
}
|
||||
|
||||
showDebugRadius()
|
||||
{
|
||||
effect["dome"] = SpawnFx( level.triDroneSettings.dome, self getTagOrigin( "tag_fx" ) );
|
||||
TriggerFx( effect["dome"] );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
effect["dome"] delete();
|
||||
}
|
||||
|
||||
showAmmoCount()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
while(1)
|
||||
{
|
||||
if("tri_drone_mp" == self GetLethalWeapon())
|
||||
{
|
||||
self SetClientOmnvar( "ui_tri_drone_count", self.triDroneAmmo );
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
createBombSquadModel( modelName, tagName, owner )
|
||||
{
|
||||
apm_bombSquadModel = spawn( "script_model", (0,0,0) );
|
||||
apm_bombSquadModel hide();
|
||||
wait ( 0.05 );
|
||||
|
||||
apm_bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( owner );
|
||||
apm_bombSquadModel setModel( modelName );
|
||||
apm_bombSquadModel linkTo( self, tagName, (0,0,0), (0,0,0) );
|
||||
apm_bombSquadModel SetContents( 0 );
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
if ( isDefined(self.trigger) )
|
||||
self.trigger delete();
|
||||
|
||||
apm_bombSquadModel delete();
|
||||
}
|
||||
|
||||
mineBeacon() // self == apm_mine
|
||||
{
|
||||
effect["friendly"] = SpawnFx( level.triDroneSettings.beacon["friendly"], self getTagOrigin( "tag_fx" ) );
|
||||
effect["enemy"] = SpawnFx( level.triDroneSettings.beacon["enemy"], self getTagOrigin( "tag_fx" ) );
|
||||
|
||||
self thread mineBeaconTeamUpdater( effect );
|
||||
self waittill( "death" );
|
||||
|
||||
effect["friendly"] delete();
|
||||
effect["enemy"] delete();
|
||||
}
|
||||
|
||||
|
||||
mineBeaconTeamUpdater( effect, effect_flare ) // self == apm_mine
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
ownerTeam = self.owner.team;
|
||||
|
||||
// PlayFXOnTag fails if run on the same frame the parent entity was created
|
||||
wait ( 0.05 );
|
||||
|
||||
TriggerFx( effect["friendly"] );
|
||||
TriggerFx( effect["enemy"] );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
effect["friendly"] Hide();
|
||||
effect["enemy"] Hide();
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
if ( player.team == ownerTeam )
|
||||
{
|
||||
effect["friendly"] ShowToPlayer( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
effect["enemy"] ShowToPlayer( player );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( player == self.owner )
|
||||
{
|
||||
effect["friendly"] ShowToPlayer( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
effect["enemy"] ShowToPlayer( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
level waittill_either ( "joined_team", "player_spawned" );
|
||||
}
|
||||
}
|
||||
|
||||
setTriDroneTeamHeadIcon( team )
|
||||
{
|
||||
self endon( "death" );
|
||||
wait .05;
|
||||
if ( level.teamBased )
|
||||
{
|
||||
if(self.IsUp==true || self.IsForward==true)
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( team, ( 0, 0, 28 ), undefined, true );
|
||||
else
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( team, ( 0, 0, 28 ) );
|
||||
}
|
||||
else if ( isDefined( self.owner ) )
|
||||
{
|
||||
if(self.IsUp==true)
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (28,0,28) );
|
||||
else
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (0,0,28) );
|
||||
}
|
||||
}
|
||||
|
||||
mineDamageMonitor() // self == apm_mine
|
||||
{
|
||||
self endon( "mine_triggered" );
|
||||
self endon( "mine_selfdestruct" );
|
||||
self endon( "death" );
|
||||
|
||||
self setcandamage( true );
|
||||
self.maxhealth = 100000;
|
||||
self.health = self.maxhealth;
|
||||
|
||||
attacker = undefined;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
if ( !isPlayer( attacker ) )
|
||||
continue;
|
||||
|
||||
// don't allow people to destroy mines on their team if FF is off
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
continue;
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
shortWeapon = maps\mp\_utility::strip_suffix( weapon, "_lefthand" );
|
||||
|
||||
switch( shortWeapon )
|
||||
{
|
||||
case "smoke_grenade_mp":
|
||||
case "smoke_grenade_var_mp":
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
self notify( "mine_destroyed" );
|
||||
|
||||
self RemoveGroupedTriDrone();
|
||||
|
||||
if ( isDefined( type ) && ( isSubStr( type, "MOD_GRENADE" ) || isSubStr( type, "MOD_EXPLOSIVE" ) ) )
|
||||
self.wasChained = true;
|
||||
|
||||
if ( isDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
self.wasDamaged = true;
|
||||
|
||||
if( isPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "bouncing_betty" );
|
||||
}
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
// "destroyed_explosive" notify, for challenges
|
||||
if ( isdefined( attacker ) && isdefined( attacker.pers[ "team" ] ) && isdefined( self.owner ) && isdefined( self.owner.pers[ "team" ] ) )
|
||||
{
|
||||
if ( attacker.pers[ "team" ] != self.owner.pers[ "team" ] )
|
||||
attacker notify( "destroyed_explosive" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// checking isDefined attacker is defensive but it's too late in the project to risk issues by not having it
|
||||
if ( isDefined( self.owner ) && isDefined( attacker ) && attacker != self.owner )
|
||||
attacker notify( "destroyed_explosive" );
|
||||
}
|
||||
|
||||
self thread mineExplode( attacker );
|
||||
}
|
||||
|
||||
mineExplode( attacker )
|
||||
{
|
||||
if ( !IsDefined( self ) || !IsDefined(self.owner) )
|
||||
return;
|
||||
|
||||
// using a passed in attacker means that the owner wasn't the one who detonated this, this way the correct credit will go to the correct player
|
||||
if( !IsDefined( attacker ) )
|
||||
attacker = self.owner;
|
||||
|
||||
self PlaySound( "null" );
|
||||
|
||||
tagOrigin = self GetTagOrigin( "tag_fx" );
|
||||
PlayFX( level.triDroneSettings.droneExplosionFx, tagOrigin );
|
||||
|
||||
wait( 0.05 ); // needed or the effect doesn't play
|
||||
if ( !IsDefined( self ) || !IsDefined(self.owner) )
|
||||
return;
|
||||
|
||||
self Hide();
|
||||
|
||||
//cause damage
|
||||
self RadiusDamage( self.origin, CONST_Mine_DamageRadius, CONST_Mine_DamageMax, CONST_Mine_DamageMin, attacker, "MOD_EXPLOSIVE", "bouncingbetty_mp" );
|
||||
|
||||
//flashbang fx
|
||||
foreach( player in level.players )
|
||||
{
|
||||
DistanceFromTriDrone = Distance(self.origin,player.origin);
|
||||
if(DistanceFromTriDrone < CONST_Mine_DamageRadius)
|
||||
{
|
||||
player ShellShock( "flashbang_mp",2.5 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( self.owner ) && IsDefined( level.leaderDialogOnPlayer_func ) )
|
||||
self.owner thread [[ level.leaderDialogOnPlayer_func ]]( "mine_destroyed", undefined, undefined, self.origin );
|
||||
|
||||
wait( 0.2 );
|
||||
if ( !IsDefined( self ) || !IsDefined(self.owner) )
|
||||
return;
|
||||
|
||||
self thread apm_mine_deleteKillCamEnt();
|
||||
|
||||
self notify ( "death" );
|
||||
|
||||
if(IsDefined(self.pickuptrigger))
|
||||
self.pickuptrigger delete();
|
||||
|
||||
self hide();
|
||||
}
|
||||
|
||||
apm_mine_deleteKillCamEnt()
|
||||
{
|
||||
wait ( 3 );
|
||||
self.killCamEnt delete();
|
||||
self delete();
|
||||
|
||||
level.mines = array_removeUndefined(level.mines);
|
||||
|
||||
}
|
||||
|
||||
|
||||
equipmentWatchUse( )
|
||||
{
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "change_owner");
|
||||
|
||||
self.pickupTrigger setCursorHint( "HINT_NOICON" );
|
||||
owner = self.pickupTrigger.owner;
|
||||
self equipmentEnableUse( owner );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self.pickupTrigger waittill ( "trigger", owner );
|
||||
|
||||
owner playLocalSound( "scavenger_pack_pickup" );
|
||||
|
||||
owner.triDroneAmmo++;
|
||||
|
||||
if(owner.triDroneAmmo==1)
|
||||
owner GiveWeapon("tri_drone_mp");
|
||||
|
||||
if(IsDefined(self.pickuptrigger))
|
||||
self.pickuptrigger delete();
|
||||
|
||||
self.killCamEnt delete();
|
||||
self delete();
|
||||
|
||||
level.mines = array_removeUndefined(level.mines);
|
||||
}
|
||||
}
|
||||
|
||||
equipmentEnableUse( owner )
|
||||
{
|
||||
self notify( "equipmentWatchUse" );
|
||||
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "equipmentWatchUse" );
|
||||
self endon( "change_owner" );
|
||||
|
||||
self.pickupTrigger setCursorHint( "HINT_NOICON" );
|
||||
|
||||
self.pickupTrigger setHintString( &"MP_PICKUP_TRI_DRONE" );
|
||||
|
||||
self.pickupTrigger setSelfUsable( owner );
|
||||
}
|
||||
|
||||
equipmentDisableUse( owner )
|
||||
{
|
||||
self.trigger setHintString( "" );
|
||||
|
||||
self.trigger setSelfUnusuable();
|
||||
}
|
||||
|
||||
mineProximityTrigger( Owner )
|
||||
{
|
||||
self endon( "mine_destroyed" );
|
||||
self endon( "mine_selfdestruct" );
|
||||
self endon( "death" );
|
||||
|
||||
wait( 2 ); //arming time
|
||||
|
||||
self.pickupTrigger = Spawn( "script_origin", self.origin );
|
||||
self.pickupTrigger.owner = Owner;
|
||||
self thread equipmentWatchUse( );
|
||||
|
||||
trigger = Spawn( "trigger_radius", (self.origin+(0,0,((CONST_Mine_TriggerHeight/2)*-1))), 0, CONST_Mine_TriggerRadius, CONST_Mine_TriggerHeight );
|
||||
trigger.owner = self;
|
||||
|
||||
self.trigger = trigger;
|
||||
|
||||
self thread mineDeleteTrigger( trigger );
|
||||
|
||||
player = undefined;
|
||||
while ( 1 )
|
||||
{
|
||||
trigger waittill( "trigger", player );
|
||||
|
||||
if(!IsDefined(player))
|
||||
break;
|
||||
|
||||
if ( getdvarint( "scr_minesKillOwner" ) != 1 )
|
||||
{
|
||||
if ( isDefined( self.owner ) )
|
||||
{
|
||||
if ( player == self.owner )
|
||||
continue;
|
||||
if ( isdefined( player.owner ) && player.owner == self.owner )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, player, 0 ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ( lengthsquared( player getEntityVelocity() ) < 10 )
|
||||
continue;
|
||||
|
||||
if ( player damageConeTrace( self.origin, self ) > 0 )
|
||||
{
|
||||
//IPrintLnBold("YESLOS");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self RemoveGroupedTriDrone();
|
||||
|
||||
self notify( "mine_triggered" );
|
||||
|
||||
self playsound( "claymore_activated" );
|
||||
|
||||
//pop it up
|
||||
self playsound( "mine_betty_spin" );
|
||||
playFX( level.mine_launch, self.origin );
|
||||
|
||||
forward = AnglesToUp( self.angles );
|
||||
explodePos = self.origin + ( forward * 64 );
|
||||
|
||||
self MoveTo( explodePos, 0.75, 0, .25 );
|
||||
self.killCamEnt MoveTo( explodePos + self.killCamOffset, 0.75, 0, .25 );
|
||||
|
||||
self RotateVelocity( (0, 750, 32), 0.7, 0, .65 );
|
||||
self thread playSpinnerFX();
|
||||
|
||||
if ( IsPlayer( player ) && player _hasPerk( "specialty_class_engineer" ) )
|
||||
{
|
||||
player notify( "triggered_mine" );
|
||||
wait CONST_Mine_TriggerDelay_Perk;
|
||||
}
|
||||
else
|
||||
wait CONST_Mine_TriggerDelay;
|
||||
|
||||
//self thread ActivateGroupedTriDrones( Owner );
|
||||
|
||||
self thread mineExplode();
|
||||
}
|
||||
|
||||
playSpinnerFX()
|
||||
{
|
||||
self endon( "death");
|
||||
|
||||
timer = gettime() + 1000;
|
||||
|
||||
while ( gettime() < timer )
|
||||
{
|
||||
wait .05;
|
||||
playFXontag( level.mine_spin, self, "tag_fx_spin1" );
|
||||
playFXontag( level.mine_spin, self, "tag_fx_spin3" );
|
||||
wait .05;
|
||||
playFXontag( level.mine_spin, self, "tag_fx_spin2" );
|
||||
playFXontag( level.mine_spin, self, "tag_fx_spin4" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mineDeleteTrigger( trigger )
|
||||
{
|
||||
self waittill_any( "mine_triggered", "mine_destroyed", "death" );
|
||||
|
||||
trigger delete();
|
||||
}
|
||||
|
||||
|
||||
mineSelfDestruct()
|
||||
{
|
||||
self endon( "mine_triggered" );
|
||||
self endon( "mine_destroyed" );
|
||||
self endon( "death" );
|
||||
|
||||
wait( CONST_Mine_TimOut );
|
||||
|
||||
self notify( "mine_selfdestruct" );
|
||||
|
||||
if ( IsDefined( self.killCamEnt ) )
|
||||
self.killCamEnt delete();
|
||||
|
||||
PlayFX( level._effect[ "equipment_explode" ], self.origin );
|
||||
self delete();
|
||||
}
|
||||
|
||||
deleteMineOnTeamSwitch( player ) // self == apm_mine
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
player endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
|
||||
player waittill ( "joined_team" );
|
||||
self Delete();
|
||||
self notify( "death" );
|
||||
}
|
||||
|
||||
handleEMP( owner, type ) // self == apm_plane || self == apm_mine
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
if ( owner isEMPed() )
|
||||
{
|
||||
self notify( "death" );
|
||||
|
||||
if ( type == "apm_mine")
|
||||
{
|
||||
PlayFX( level._effect[ "equipment_explode" ], self.origin );
|
||||
self Delete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "emp_update" );
|
||||
|
||||
if ( !owner isEMPed() )
|
||||
continue;
|
||||
|
||||
if ( type == "apm_mine")
|
||||
{
|
||||
PlayFX( level._effect[ "equipment_explode" ], self.origin );
|
||||
self Delete();
|
||||
|
||||
}
|
||||
|
||||
self notify( "death" );
|
||||
}
|
||||
}
|
7104
raw/maps/mp/_utility.gsc
Normal file
7104
raw/maps/mp/_utility.gsc
Normal file
File diff suppressed because it is too large
Load Diff
556
raw/maps/mp/_vl_base.gsc
Normal file
556
raw/maps/mp/_vl_base.gsc
Normal file
@ -0,0 +1,556 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
//#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_vl_camera;
|
||||
#include maps\mp\_vl_firingrange;
|
||||
#include maps\mp\_vl_selfiebooth;
|
||||
|
||||
PARTYSTATUS_EMPTY = 0;
|
||||
PARTYSTATUS_ZOMBIE = 1;
|
||||
PARTYSTATUS_ANONYMOUS = 2;
|
||||
PARTYSTATUS_JOINED = 3;
|
||||
PARTYSTATUS_COMMITTED = 4;
|
||||
PARTYSTATUS_PRESENT = 5;
|
||||
|
||||
|
||||
vlprint( str )
|
||||
{
|
||||
print( "VLOBBY["+GetTime()+"]: " + str );
|
||||
}
|
||||
|
||||
vlprintln( str )
|
||||
{
|
||||
println( "VLOBBY["+GetTime()+"]: " + str );
|
||||
}
|
||||
|
||||
vl_init()
|
||||
{
|
||||
level.vl_onSpawnPlayer = ::onSpawnPlayer;
|
||||
vl_main();
|
||||
}
|
||||
|
||||
|
||||
vl_main()
|
||||
{
|
||||
SetDvar("r_dof_physical_enable", 1);
|
||||
SetDvar("r_dof_physical_bokehEnable", 1);
|
||||
SetDvar("r_adaptiveSubdiv", 0);
|
||||
SetDvar("r_eyePupil", 0.15);
|
||||
SetDvar("r_uiblurdstmode", 3);
|
||||
SetDvar("r_blurdstgaussianblurradius", 1.5);
|
||||
ResetEntPlayerXuidForEmblems();
|
||||
|
||||
//level.PartyMemberClassChange_cb = ::PartyMemberClassChange;
|
||||
level.PartyMembers_cb = ::party_members;
|
||||
level.vlavatars = [];
|
||||
level.xuid2ownerId = [];
|
||||
level.vl_focus = 0;
|
||||
level.avatarInfo = [];
|
||||
maxAvatars = 18;
|
||||
if ( level.ps3 || level.xenon )
|
||||
maxAvatars = 12;
|
||||
for ( i=0; i<maxAvatars; i++ )
|
||||
{
|
||||
level.avatarInfo[i] = SpawnStruct();
|
||||
level.avatarInfo[i].timeToDelete = 0;
|
||||
level.avatarInfo[i].xuid = "";
|
||||
}
|
||||
level.maxAvatars = maxAvatars;
|
||||
|
||||
thread monitor_member_class_changes();
|
||||
thread monitor_member_timeouts();
|
||||
|
||||
SetDvar( "virtuallobbymembers", 0 ); // ensure this is cleared whenever we enter the vl
|
||||
/#
|
||||
SetDvarIfUninitialized( "scr_vl_addfakemembers", "0" );
|
||||
SetDvarIfUninitialized( "scr_vl_debugfly", "0" );
|
||||
SetDvarIfUninitialized( "scr_vl_printcostume", "0" );
|
||||
level.vl_fakemembers = 0;
|
||||
#/
|
||||
level.num_lobby_idles = 4;
|
||||
init_firingrange();
|
||||
init_selfiebooth();
|
||||
//lighying values
|
||||
//level.sunpitch_damage = 0;
|
||||
//waitframe();
|
||||
//level.sunpitch = GetEnt("sun_pitch_swap", "targetname");
|
||||
//level.sunpitch_origin = level.sunpitch getOrigin();
|
||||
}
|
||||
|
||||
init_avatars()
|
||||
{
|
||||
EXTRA_AVATARS = 1; // for cao
|
||||
maxAvatars = level.maxAvatars + EXTRA_AVATARS;
|
||||
level.vlavatarpool = [];
|
||||
for ( i=0; i<maxAvatars; i++ )
|
||||
{
|
||||
avatar = getFreeAgent();
|
||||
level.vlavatarpool[i] = avatar;
|
||||
avatar SpawnAgent( (0,0,0), (0,0,0) );
|
||||
avatar set_agent_values( "spectator", "none" );
|
||||
avatar maps\mp\agents\_agent_common::set_agent_health( 100 );
|
||||
|
||||
avatar BotClearScriptEnemy();
|
||||
avatar BotClearScriptGoal();
|
||||
avatar bot_disable_tactical_goals();
|
||||
avatar BotSetFlag("disable_movement", 1);
|
||||
avatar BotSetFlag("disable_rotation",1); // they shouldn't try and rotate
|
||||
avatar.isfree = true;
|
||||
}
|
||||
}
|
||||
|
||||
alloc_avatar()
|
||||
{
|
||||
if ( !IsDefined(level.vlavatarpool) )
|
||||
init_avatars();
|
||||
|
||||
foreach ( avatar in level.vlavatarpool )
|
||||
{
|
||||
if ( avatar.isfree )
|
||||
{
|
||||
avatar.isfree = false;
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
assertmsg( "Unable to alloc_avatar" );
|
||||
}
|
||||
|
||||
free_avatar( avatar )
|
||||
{
|
||||
assert( !avatar.isfree );
|
||||
avatar notify( "free_avatar" );
|
||||
avatar.isfree = true;
|
||||
}
|
||||
|
||||
onSpawnPlayer()
|
||||
{
|
||||
level.agent_funcs["player"]["on_killed"] = ::on_agent_player_killed;
|
||||
self thread vlobby_player();
|
||||
level.playerStatus[0] = PARTYSTATUS_JOINED;
|
||||
self thread monitor_member_focus_change();
|
||||
self thread monitor_create_an_operator(0);
|
||||
self thread monitor_create_a_class(0);
|
||||
self thread monitor_clans();
|
||||
thread monitor_move_btn_fr_vl( self );
|
||||
self disable_player_controls();
|
||||
// TODO needs to replinish grenade after player uses one in firing range. It's weird after you use one and hold it too long nothing happens but it eats your grenade.
|
||||
self SetDemiGod(true); /// needed to skip mod_suicide death call from holding a grenade too long. Grenade suicide called in code
|
||||
self SetMLGSpectator( 0 ); // needed to catch a bug where we don't properly clear this when the host of a game ends the match while in MLG Broadcaster
|
||||
}
|
||||
|
||||
on_agent_player_killed(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
// don't do anything
|
||||
}
|
||||
|
||||
|
||||
player_sticks_in_lefty_config()
|
||||
{
|
||||
if ( self is_player_gamepad_enabled() )
|
||||
{
|
||||
sticksLayoutName = self GetLocalPlayerProfileData( "gpadSticksConfig" );
|
||||
return IsDefined( sticksLayoutName ) && ( ( sticksLayoutName == "thumbstick_southpaw" ) || ( sticksLayoutName == "thumbstick_legacy" ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
player_setup_lefty_angle( avatar )
|
||||
{
|
||||
if ( !IsDefined( avatar.fakeAngle ) )
|
||||
{
|
||||
if ( IsDefined( avatar.storedRightStickY ) )
|
||||
{
|
||||
avatar.fakeAngle = avatar.storedRightStickY;
|
||||
}
|
||||
else
|
||||
{
|
||||
avatar.fakeAngle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player_get_right_stick_y( avatar )
|
||||
{
|
||||
if ( self player_sticks_in_lefty_config() )
|
||||
{
|
||||
player_setup_lefty_angle( avatar );
|
||||
return avatar.fakeAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
rightStick = self GetUnNormalizedCameraMovement();
|
||||
return rightStick[1];
|
||||
}
|
||||
}
|
||||
|
||||
player_update_right_stick_y( avatar )
|
||||
{
|
||||
|
||||
rightStickY = 0;
|
||||
if ( self player_sticks_in_lefty_config() )
|
||||
{
|
||||
player_setup_lefty_angle( avatar );
|
||||
|
||||
rightStick = self GetNormalizedMovement();
|
||||
rotSpeed = -12;
|
||||
rotDelta = rightStick[1] * rotSpeed;
|
||||
avatar.fakeAngle = AngleClamp( avatar.fakeAngle + rotDelta );
|
||||
rightStickY = avatar.fakeAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
rightStick = self GetUnNormalizedCameraMovement();
|
||||
rightStickY = rightStick[1];
|
||||
|
||||
if ( IsDefined( avatar.fakeAngle ) )
|
||||
{
|
||||
avatar.fakeAngle = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return rightStickY;
|
||||
}
|
||||
|
||||
prep_for_controls( avatar, set_avatar_angles )
|
||||
{
|
||||
avatar.storedRightStickY = self player_get_right_stick_y( avatar ); // storing where right stick is so we can track changes
|
||||
avatar.rotation_total = 0; // setting difference in right stick change
|
||||
avatar.storedangleY = avatar.angles[1];
|
||||
avatar.mouseRot = 0;
|
||||
avatar.positivewrap = 0;
|
||||
avatar.negativewrap = 0;
|
||||
avatar.addtobaseangle = 0;
|
||||
set_avatar_angles = (0, set_avatar_angles[1], 0); // forcing X and Z. Avatar should only spin
|
||||
if(IsDefined(set_avatar_angles))
|
||||
{
|
||||
if(IsAgent(avatar))
|
||||
{
|
||||
avatar HackAgentAngles(set_avatar_angles);
|
||||
}
|
||||
else
|
||||
avatar.angles = set_avatar_angles;
|
||||
}
|
||||
}
|
||||
|
||||
avatar_scheduled_for_removal( ownerid )
|
||||
{
|
||||
assert( level.avatarInfo[ownerid].timeToDelete == 0 ); // this should never get set now that we never truly free avatars
|
||||
return level.avatarInfo[ownerid].timeToDelete > 0;
|
||||
}
|
||||
|
||||
schedule_remove_avatar( ownerid, dly )
|
||||
{
|
||||
remove_avatar( ownerid );
|
||||
}
|
||||
|
||||
all_avatars_scheduled_for_delete()
|
||||
{
|
||||
if (level.vlavatars.size == 0)
|
||||
return false;
|
||||
foreach ( ownerId, avatarInfo in level.avatarInfo )
|
||||
{
|
||||
if ( !IsDefined( level.vlavatars[ownerId] ) )
|
||||
continue;
|
||||
if ( avatarInfo.timeToDelete == 0 )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
reuse_avatar( xuid )
|
||||
{
|
||||
assert( level.vlavatars.size == 1 );
|
||||
avatar = undefined;
|
||||
foreach ( xuidtmp, idx in level.xuid2ownerId )
|
||||
{
|
||||
avatar = level.vlavatars[idx];
|
||||
level.vlavatars[idx] = undefined;
|
||||
setEntPlayerXuidForEmblem( avatar, xuidtmp, true ); // release this xuid
|
||||
level.xuid2ownerId[xuidtmp] = undefined;
|
||||
}
|
||||
|
||||
level.xuid2ownerId[xuid] = 0;
|
||||
level.avatarInfo[0].timeToDelete = 0;
|
||||
level.avatarInfo[0].xuid = xuid;
|
||||
if ( isDefined(avatar) )
|
||||
{
|
||||
setEntPlayerXuidForEmblem( avatar, xuid ); // associate with new xuid
|
||||
level.vlavatars[0] = avatar;
|
||||
}
|
||||
}
|
||||
|
||||
add_avatar( xuid )
|
||||
{
|
||||
// first find an unused ownerId
|
||||
ownerId = -1;
|
||||
oId = -1;
|
||||
while (oId == ownerId)
|
||||
{
|
||||
ownerId++;
|
||||
foreach ( oId in level.xuid2ownerId )
|
||||
{
|
||||
if (oId == ownerId)
|
||||
break;
|
||||
}
|
||||
}
|
||||
vlprint( "Adding new xuid "+xuid+" with ownerId="+ownerId+"\n");
|
||||
level.xuid2ownerId[xuid] = ownerId;
|
||||
level.avatarInfo[ownerId].xuid = xuid;
|
||||
level.avatarInfo[ownerId].timeToDelete = 0;
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
avatar_after_spawn( ownerId )
|
||||
{
|
||||
if ( isdefined(level.needToPresent) )
|
||||
{
|
||||
thread SetVirtualLobbyPresentable();
|
||||
}
|
||||
}
|
||||
|
||||
update_avatars()
|
||||
{
|
||||
anyRemoved = false;
|
||||
foreach ( ownerId, avatarInfo in level.avatarInfo )
|
||||
{
|
||||
if ( ( avatarInfo.timeToDelete > 0 ) && ( GetTime() > avatarInfo.timeToDelete ) )
|
||||
{
|
||||
vlprint( "update_avatars removing ownerId"+ownerId + "\n");
|
||||
remove_avatar( ownerId );
|
||||
anyRemoved = true;
|
||||
}
|
||||
}
|
||||
if ( anyRemoved )
|
||||
wait 0.1; // to deal with an issue where we can't spawn an agent we just deleted.
|
||||
}
|
||||
|
||||
hide_non_owner_avatars()
|
||||
{
|
||||
foreach ( idx, avatar in level.vlavatars )
|
||||
{
|
||||
if ( idx == 0 )
|
||||
continue;
|
||||
hide_avatar( avatar );
|
||||
}
|
||||
if(level.camParams.mode != "prelobby") // camera cut if we are not in pre lobby
|
||||
{
|
||||
level.camParams.camera.cut = true;
|
||||
}
|
||||
|
||||
level.vl_focus = 0;
|
||||
level.old_vl_focus = level.vl_focus;
|
||||
|
||||
}
|
||||
|
||||
show_non_owner_avatars()
|
||||
{
|
||||
foreach ( idx, avatar in level.vlavatars )
|
||||
{
|
||||
if ( idx == 0 )
|
||||
continue;
|
||||
show_avatar( avatar );
|
||||
}
|
||||
}
|
||||
|
||||
RightStickRotateAvatar(targetAvatar, ratio)
|
||||
{
|
||||
rightStickY = self player_update_right_stick_y( targetAvatar );
|
||||
rightStickDiffY = AngleClamp(rightStickY - targetAvatar.storedRightStickY);
|
||||
|
||||
mouseRot = GetDvarFloat( "ui_mouse_char_rot", 0 );
|
||||
if ( mouseRot != 0 )
|
||||
{
|
||||
targetAvatar.mouseRot = AngleClamp(targetAvatar.mouseRot + mouseRot);
|
||||
SetDynamicDvar( "ui_mouse_char_rot", 0 ); // rotation is "consumed", reset it, UI will continue adding each frame
|
||||
}
|
||||
|
||||
AddtoAngle = GetModifiedRotationAngle(targetAvatar, rightStickDiffY, ratio);
|
||||
AddtoAngle *= -1; // reversing rotation
|
||||
AvatarYAngle = AngleClamp(targetAvatar.storedangleY + AddtoAngle + targetAvatar.mouseRot);
|
||||
avatar_angles = ( 0, AvatarYAngle, 0 ); // forcing X and Z. Avatar should only spin
|
||||
// targetAvatar SetPlayerAngles(avatar_angles);
|
||||
if(IsAgent(targetAvatar))
|
||||
{
|
||||
// targetAvatar ScrAgentSetOrientMode("face angle abs", avatar_angles, avatar_angles); // doesn't work anymore on agents?
|
||||
targetAvatar SetPlayerAngles(avatar_angles);
|
||||
}
|
||||
else
|
||||
{
|
||||
// for CAC weapons or other non agents
|
||||
targetAvatar.angles = avatar_angles;
|
||||
}
|
||||
}
|
||||
PlayerHasTouchedStick(targetAvatar)
|
||||
{
|
||||
rightStickY = self player_get_right_stick_y( targetAvatar );
|
||||
rightStickDiffY = AngleClamp(rightStickY - targetAvatar.storedRightStickY);
|
||||
if(self.HasTouchedStick == false)
|
||||
{
|
||||
if(abs(rightStickDiffY) >= 1 )
|
||||
{
|
||||
targetAvatar.storedangleY = targetAvatar.angles[1];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// targetAvatar.storedangleY = targetAvatar.angles;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
disable_player_controls()
|
||||
{
|
||||
self notify("kill_enable_weapons");
|
||||
// self freezeControlsWrapper(true);
|
||||
|
||||
self AllowFire(false);
|
||||
// self DisableWeapons();
|
||||
// self DisableOffhandWeapons();
|
||||
// self DisableWeaponSwitch();
|
||||
|
||||
}
|
||||
|
||||
enable_player_controls()
|
||||
{
|
||||
self endon("enter_lobby");
|
||||
self endon("kill_enable_weapons");
|
||||
|
||||
// self freezeControlsWrapper(false);
|
||||
|
||||
// wait(2);
|
||||
|
||||
val = GetDvarInt( "virtualLobbyInFiringRange", 0 );
|
||||
if ( ( val == 1 ) && level.in_firingrange == true)
|
||||
{
|
||||
// self freezeControlsWrapper(false);
|
||||
self AllowFire(true);
|
||||
// self EnableWeapons();
|
||||
// self EnableOffhandWeapons();
|
||||
// self EnableWeaponSwitch();
|
||||
}
|
||||
|
||||
// self playerAllowHighJump(false);
|
||||
// self playerAllowHighJumpDrop(false);
|
||||
// self playerAllowBoostJump(false);
|
||||
// self playerAllowPowerSlide(false);
|
||||
// self playerAllowDodge(false);
|
||||
|
||||
}
|
||||
|
||||
enter_vlobby( player )
|
||||
{
|
||||
deactivate_targets();
|
||||
//SetDvar( "virtualLobbyInFiringRange", "0" );
|
||||
|
||||
camera = player.camera;
|
||||
player SetOrigin(camera.origin);
|
||||
player PlayerLinkTo( camera, "tag_player" );
|
||||
player CameraLinkTo( camera, "tag_player" );
|
||||
player SetClientDVar( "cg_fovscale", "0.6153" );
|
||||
|
||||
//player VisionSetNakedForPlayer("mp_virtual_lobby", 0);
|
||||
player VisionSetNakedForPlayer("mp_virtual_lobby_cac", 0);
|
||||
//player LightSetForPlayer("mp_vl_create_a_class");
|
||||
|
||||
if(isdefined(level.vlavatars) && isdefined(level.old_vl_focus) && isdefined(level.vlavatars[level.old_vl_focus]))
|
||||
{
|
||||
player prep_for_controls(level.vlavatars[level.old_vl_focus], level.vlavatars[level.old_vl_focus].angles);
|
||||
}
|
||||
|
||||
level.in_firingrange = false;
|
||||
player AllowFire(false);
|
||||
updateSessionState("spectator");
|
||||
}
|
||||
|
||||
monitor_move_btn_fr_vl( player )
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if ( isDefined( level.in_firingrange ) )
|
||||
{
|
||||
val = GetDvarInt( "virtualLobbyInFiringRange", 0 );
|
||||
if ( ( val == 1 ) && !level.in_firingrange )
|
||||
{
|
||||
// attempting to wait until weapon model is actually loaded
|
||||
class_num = getClassIndex( "lobby"+(player.currentSelectedClass+1) );
|
||||
classLoc = cac_getCustomClassLoc();
|
||||
loadout = player.loadouts[classLoc][class_num];
|
||||
loadoutPrimary = loadout["primary"];
|
||||
loadoutSecondary = loadout["secondary"];
|
||||
|
||||
if(isdefined(level.vlavatars) && isdefined(level.old_vl_focus) && isdefined(level.vlavatars[level.old_vl_focus]))
|
||||
{
|
||||
player prep_for_controls(level.vlavatars[level.old_vl_focus], level.vlavatars[level.old_vl_focus].angles);
|
||||
}
|
||||
|
||||
WeaponNames = [];
|
||||
{
|
||||
if(isdefined(loadoutPrimary) && loadoutPrimary !="specialty_null")
|
||||
{
|
||||
WeaponNames[WeaponNames.size] = maps\mp\gametypes\_class::buildWeaponName( loadoutPrimary, loadout["primaryAttachment1"], loadout["primaryAttachment2"], loadout["primaryAttachment3"], 0, 0 );
|
||||
}
|
||||
if(isdefined(loadoutSecondary) && loadoutSecondary !="specialty_null")
|
||||
{
|
||||
WeaponNames[WeaponNames.size] = maps\mp\gametypes\_class::buildWeaponName( loadoutSecondary, loadout["secondaryAttachment1"], loadout["secondaryAttachment2"], loadout["secondaryAttachment3"], 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
while(WeaponNames.size > 0)
|
||||
{
|
||||
HasLoadededWeapons = player loadweapons(WeaponNames);
|
||||
if(HasLoadededWeapons == true)
|
||||
{
|
||||
break;
|
||||
}
|
||||
wait(0.05);
|
||||
}
|
||||
|
||||
player ShowViewModel();
|
||||
|
||||
enter_firingrange( player );
|
||||
player clientclearsoundsubmix( "mp_no_foley", 1 );
|
||||
SetDvar("r_dof_physical_bokehEnable", 0);
|
||||
SetDvar("r_dof_physical_enable", 0);
|
||||
SetDvar("r_uiblurdstmode", 0);
|
||||
SetDvar("r_blurdstgaussianblurradius", 1);
|
||||
}
|
||||
else if ( ( val == 0 ) && level.in_firingrange )
|
||||
{
|
||||
player HideViewModel();
|
||||
player FiringRangeCleanup();
|
||||
player disable_player_controls();
|
||||
|
||||
player notify("enter_lobby");
|
||||
enter_vlobby( player );
|
||||
player clientaddsoundsubmix( "mp_no_foley", 1 );
|
||||
SetDvar("r_dof_physical_enable", 1);
|
||||
SetDvar("r_dof_physical_bokehEnable", 1);
|
||||
SetDvar("r_uiblurdstmode", 3);
|
||||
SetDvar("r_blurdstgaussianblurradius", 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
FiringRangeCleanup()
|
||||
{
|
||||
player = self;
|
||||
player GrenadeCleanup(); // clean up grenades left in firing range
|
||||
player thread RiotShieldCleanup();
|
||||
weaponListOffhand = player GetWeaponsListOffhands();
|
||||
foreach( weapon in weaponListOffhand )
|
||||
{
|
||||
player maps\mp\gametypes\_class::takeOffhand( weapon ); // remove any exo abilities
|
||||
}
|
||||
}
|
||||
|
||||
|
4470
raw/maps/mp/_vl_camera.gsc
Normal file
4470
raw/maps/mp/_vl_camera.gsc
Normal file
File diff suppressed because it is too large
Load Diff
2410
raw/maps/mp/_vl_firingrange.gsc
Normal file
2410
raw/maps/mp/_vl_firingrange.gsc
Normal file
File diff suppressed because it is too large
Load Diff
158
raw/maps/mp/_vl_selfiebooth.gsc
Normal file
158
raw/maps/mp/_vl_selfiebooth.gsc
Normal file
@ -0,0 +1,158 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
//#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_vl_camera;
|
||||
#include maps\mp\_vl_base;
|
||||
|
||||
#using_animtree( "multiplayer_vlobby" );
|
||||
|
||||
//---- SELFIE BOOTH ----//
|
||||
|
||||
init_selfieBooth()
|
||||
{
|
||||
player_pos = GetEnt("selfie_player_pos","targetname");
|
||||
if(!IsDefined(player_pos))
|
||||
return;
|
||||
|
||||
|
||||
camera_target = GetEnt(player_pos.target, "targetname");
|
||||
camera_pos = GetEnt(camera_target.target, "targetname");
|
||||
|
||||
level.selfieBooth = SpawnStruct();
|
||||
level.selfieBooth.player_pos = player_pos;
|
||||
level.selfieBooth.camera_pos = camera_pos;
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized("scr_vlobby_force_selfie", 0);
|
||||
#/
|
||||
|
||||
SetDvarIfUninitialized("scr_vlobby_selfie_correction_y", 32.0);
|
||||
SetDvarIfUninitialized("scr_vlobby_selfie_collision_z", 10.0);
|
||||
|
||||
level thread run_selfieBooth();
|
||||
}
|
||||
|
||||
run_selfieBooth()
|
||||
{
|
||||
//Wait for local player
|
||||
while(!isDefined(level.player))
|
||||
waitframe();
|
||||
|
||||
Assert(level.player IsHost());
|
||||
|
||||
level.player SpawnSelfieAvatar();
|
||||
|
||||
wait 5; //Debug to make sure we dont take the selfie too soon.
|
||||
|
||||
while(1)
|
||||
{
|
||||
waitframe();
|
||||
if ( IsSystemLink() || !IsOnlineGame() || GetDvarInt( "practiceroundgame" ) )
|
||||
continue; // no selfies in system link, local play or CRP
|
||||
|
||||
if(!should_take_selfie())
|
||||
continue;
|
||||
|
||||
take_selfie();
|
||||
}
|
||||
}
|
||||
|
||||
SpawnSelfieAvatar()
|
||||
{
|
||||
while(!isdefined(level.player.spawned_avatar) || !isdefined(level.player.spawned_avatar.costume))
|
||||
{
|
||||
waitframe();
|
||||
}
|
||||
|
||||
clone_origin = level.selfieBooth.player_pos.origin;
|
||||
|
||||
Z = GetDvarFloat("scr_vlobby_selfie_collision_z", 0.0);
|
||||
Y = GetDvarFloat("scr_vlobby_selfie_correction_y", 0.0);
|
||||
|
||||
clone_footPos = (0,Y,Z);
|
||||
|
||||
clone_origin += clone_footPos;
|
||||
|
||||
clone_angles = level.selfieBooth.player_pos.angles;
|
||||
|
||||
clone = getFreeAgent( "selfie_clone" );
|
||||
clone.isActive = true;
|
||||
clone SpawnAgent( clone_origin, clone_angles, undefined, undefined, undefined, undefined, true );
|
||||
xuid = level.player getxuid();
|
||||
setEntPlayerXuidForEmblem( clone, xuid );
|
||||
|
||||
clone EnableAnimState( true );
|
||||
clone SetAnimClass( "vlobby_animclass" );
|
||||
clone SetAnimState( "lobby_idle", "selfie_01", 1.0 );
|
||||
|
||||
clone SetCostumeModels( level.player.spawned_avatar.costume );
|
||||
|
||||
clone LinkTo(level.selfieBooth.player_pos);
|
||||
|
||||
level.selfieBooth.clone = clone;
|
||||
self.selfie_clone = clone;
|
||||
}
|
||||
|
||||
should_take_selfie()
|
||||
{
|
||||
if(!IsDefined(level.player))
|
||||
return false;
|
||||
|
||||
if ( !isdefined(level.player.spawned_avatar) || !isdefined(level.player.spawned_avatar.costume) )
|
||||
return false;
|
||||
|
||||
if( !level.player selfieAccessSelfieCustomAssetsAreStreamed() )
|
||||
return false;
|
||||
|
||||
/#
|
||||
|
||||
if( GetDvarInt("scr_vlobby_force_selfie", 0) )
|
||||
{
|
||||
SetDevDvar("scr_vlobby_force_selfie", 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
#/
|
||||
|
||||
if( level.player selfieAccessSelfieValidFlagInPlayerDef() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
take_selfie()
|
||||
{
|
||||
if(!IsDefined(level.selfieBooth.clone))
|
||||
return;
|
||||
|
||||
if(!IsDefined(level.player))
|
||||
return;
|
||||
|
||||
if(!IsDefined(level.player.spawned_avatar))
|
||||
return;
|
||||
|
||||
if(!IsDefined(level.player.spawned_avatar.costume))
|
||||
return;
|
||||
|
||||
|
||||
clone_origin = level.selfieBooth.clone.origin;
|
||||
|
||||
clone_eyePos = level.selfieBooth.clone GetEye();
|
||||
|
||||
level.selfieBooth.clone SetCostumeModels( level.player.spawned_avatar.costume );
|
||||
|
||||
waitframe(); // wait for costume update
|
||||
|
||||
if( !level.player SelfieRequestUpdate(level.selfieBooth.camera_pos.origin, clone_origin, clone_eyePos[2]-clone_origin[2], 0,0 ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while( isDefined(level.player) && !level.player SelfieScreenshotTaken() )
|
||||
{
|
||||
waitframe();
|
||||
}
|
||||
}
|
690
raw/maps/mp/_water.gsc
Normal file
690
raw/maps/mp/_water.gsc
Normal file
@ -0,0 +1,690 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\_art;
|
||||
|
||||
CONST_SWIMMING_DEPTH = 48;
|
||||
|
||||
init()
|
||||
{
|
||||
/*---HOW TO SET UP----/
|
||||
|
||||
In Script:
|
||||
* add this line to your map.gsc main() function: maps\mp\_water::init();
|
||||
* Add this to your level.csv file:
|
||||
* include,_water
|
||||
In Radiant:
|
||||
* In your level in radiant add a trigger_multiple with a KVP of: "targetname" / "trigger_underwater". Place the trigger where you want the player to be considered touching the water.
|
||||
* give the trigger_multiple a KVP of: "script_noteworthy" / "<name>". Name can be anything.
|
||||
* link the trigger_multiple to a script struct. Place the script struct at the waterline.
|
||||
* In your level in radiant add a client_trigger that encompasses the water (about 8 units below the ground and about 64 units above the waterline). Give it a KVP of "script_water" / <name>. the name needs to match the script_noteworthy of the trigger_multiple.
|
||||
|
||||
|
||||
/----OPTIONAL----/
|
||||
|
||||
Customize: the lightset, fogset, vfx, sound fx, visionset, and depth of field. To do this:
|
||||
* Settings for the water triggers should be added to the <mapname>_water.csv file. That csv file should be included in <mapname>.csv as a stringtable type.
|
||||
* stringtable,maps/createart/<mapname>_water.csv
|
||||
* You can copy maps/createart/default_water.csv to start with and add rows as needed. Please make a copy and do not edit the default as it would effect other levels. The name column should be filled in with whatever you used for the script_water KVP in the client trigger.
|
||||
* Columns:
|
||||
* plunge vfx/sfx - The vfx/sfx to play when going under the water.
|
||||
* emerge vfx/sfx - The vfx/sfx to play when coming out of the water.
|
||||
* bubble vfx - The vfx to play while underwater.
|
||||
* bubble update ms - The bubble vfx play over and over, this is the time in mS between successive plays of the vfx.
|
||||
* breath sfx - The "player breathing heavily" sound to play when emerging.
|
||||
* underwater vision set - The name of the vision set to apply when underwater. (from <mapname>_fog.gsc and <mapname>_fog_hdr.gsc files).
|
||||
* underwater light set - The name of the light set to apply when underwater. ( from <mapname>_lightsets_hdr.gsc )
|
||||
* dof* - The depth of field settings to apply when underwater.
|
||||
|
||||
Moving water:
|
||||
* If your water is animated or moves you can link the waterline ent(s) to the animated or moving water.
|
||||
* The water ents are stored in level.waterline_ents which is an array of tag_origin script models that are created in script based on the location of the script_struct you place in radiant.
|
||||
* To identify a specific one use the .script_noteworthy of one of the waterline_ents in the array. The script noteworthy of it will match the script noteworthy of the trigger_multiple you placed in radiant in step 2 above.
|
||||
|
||||
*TODO: make swimming better, and add dolphin death! (for map boundaries or map killstreak)
|
||||
/---------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
level._effect[ "water_wake" ] = loadfx( "vfx/treadfx/body_wake_water" );
|
||||
level._effect[ "water_wake_stationary" ] = loadfx( "vfx/treadfx/body_wake_water_stationary" );
|
||||
level._effect[ "water_splash_emerge" ] = loadfx( "vfx/water/body_splash_exit" );
|
||||
level._effect[ "water_splash_enter" ] = loadfx( "vfx/water/body_splash" );
|
||||
|
||||
PreCacheShellShock( "underwater" );
|
||||
|
||||
if( !isDefined( level.waterline_ents ) )
|
||||
level.waterline_ents = [];
|
||||
|
||||
if( !isDefined( level.waterline_offset ) )
|
||||
level.waterline_offset = 0;
|
||||
if( !isDefined( level.shallow_water_weapon ) )
|
||||
SetShallowWaterWeapon( "iw5_combatknife_mp" );
|
||||
if( !isDefined( level.deep_water_weapon ) )
|
||||
SetDeepWaterWeapon( "iw5_underwater_mp" );
|
||||
if( !isDefined( level.allow_swimming ) )
|
||||
level.allow_swimming = true;
|
||||
if( level.deep_water_weapon == level.shallow_water_weapon )
|
||||
level.allow_swimming = false;
|
||||
if( !isDefined( level.swimming_depth ) )
|
||||
level.swimming_depth = CONST_SWIMMING_DEPTH;
|
||||
|
||||
triggers = getentarray( "trigger_underwater", "targetname" );
|
||||
level.water_triggers = triggers;
|
||||
|
||||
foreach( trig in triggers )
|
||||
{
|
||||
trig create_clientside_water_ents();
|
||||
trig thread watchPlayerEnterWater();
|
||||
level thread clearWaterVarsOnSpawn( trig );
|
||||
}
|
||||
|
||||
level thread OnPlayerConnectFunctions();
|
||||
}
|
||||
|
||||
player_set_in_water( in_water )
|
||||
{
|
||||
if ( in_water )
|
||||
{
|
||||
self.inWater = true;
|
||||
if ( IsAIGameParticipant( self ) )
|
||||
self BotSetFlag("in_water",true);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.inWater = undefined;
|
||||
if ( IsAIGameParticipant( self ) )
|
||||
self BotSetFlag("in_water",false);
|
||||
}
|
||||
}
|
||||
|
||||
OnPlayerConnectFunctions()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
foreach( waterline_ent in level.waterline_ents )
|
||||
{
|
||||
player InitWaterClientTrigger( waterline_ent.script_noteworthy, waterline_ent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_clientside_water_ents()
|
||||
{
|
||||
water_ent_struct = getstruct( self.target, "targetname" );
|
||||
AssertEx( isDefined( water_ent_struct ), "waterline needs to be defined. Place a script_struct at the height of the waterline and link your underwater trigger to it." );
|
||||
|
||||
water_ent_struct.origin = water_ent_struct.origin + (0,0,level.waterline_offset);
|
||||
|
||||
waterline_ent = water_ent_struct spawn_tag_origin();
|
||||
waterline_ent show();
|
||||
|
||||
if( isDefined( self.script_noteworthy ) )
|
||||
{
|
||||
waterline_ent.script_noteworthy = self.script_noteworthy;
|
||||
level.waterline_ents = array_add( level.waterline_ents, waterline_ent );
|
||||
}
|
||||
}
|
||||
|
||||
clearWaterVarsOnSpawn( underWater )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "player_spawned", player );
|
||||
|
||||
if ( !player IsTouching( underWater ) )
|
||||
{
|
||||
player player_set_in_water(false);
|
||||
player.underWater = undefined;
|
||||
player.inThickWater = undefined;
|
||||
player.isSwimming = undefined;
|
||||
player.isWading = undefined;
|
||||
player.water_last_weapon = undefined;
|
||||
player.isShocked = undefined;
|
||||
player notify( "out_of_water" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchPlayerEnterWater()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "trigger", ent );
|
||||
|
||||
if( IsDefined(level.isHorde) && level.isHorde && IsAgent( ent ) && IsDefined( ent.horde_type ) && ent.horde_type == "Quad" && !isDefined(ent.inWater))
|
||||
ent thread hordeDogInWater( self );
|
||||
|
||||
if ( !IsPlayer( ent ) && !IsAI(ent) )
|
||||
continue;
|
||||
|
||||
if ( !isAlive( ent ) )
|
||||
continue;
|
||||
|
||||
if ( !isDefined( ent.inWater ) )
|
||||
{
|
||||
ent player_set_in_water(true);
|
||||
ent thread playerInWater( self );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hordeDogInWater(trig)
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self player_set_in_water(true);
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if (!self inShallowWater( trig, 40 ))
|
||||
{
|
||||
wait 2.5;
|
||||
if( !self inShallowWater( trig, 20 ))
|
||||
self DoDamage( self.health, self.origin );
|
||||
}
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
playerInWater( underWater )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self thread inWaterWake(underWater);
|
||||
self thread playerWaterClearWait();
|
||||
|
||||
self.eyeHeightLastFrame = 0;
|
||||
self.eye_velocity = 0;
|
||||
|
||||
//note: go from out of the water to in for the order in the if statements below
|
||||
while( true )
|
||||
{
|
||||
if ( self maps\mp\_utility::isUsingRemote() )
|
||||
{
|
||||
if ( isDefined( self.underWater ) && isDefined( self.isShocked ) )
|
||||
{
|
||||
self StopShellShock();
|
||||
self.isShocked = undefined;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( isDefined( self.underWater ) && !IsDefined( self.isShocked ) )
|
||||
{
|
||||
self shellShock( "underwater", 19 );
|
||||
self.isShocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
//player is not touching the water
|
||||
if ( !self IsTouching( underWater ) )
|
||||
{
|
||||
self player_set_in_water(false);
|
||||
self.underWater = undefined;
|
||||
self.inThickWater = undefined;
|
||||
self.isSwimming = undefined;
|
||||
self.moveSpeedScaler = level.basePlayerMoveScale;
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
self notify( "out_of_water" );
|
||||
break;
|
||||
}
|
||||
|
||||
//player is walking in shallow water after being in thick water
|
||||
if( isDefined( self.inThickWater ) && self inShallowWater( underwater, 32 ) )
|
||||
{
|
||||
self.inThickWater = undefined;
|
||||
self.moveSpeedScaler = level.basePlayerMoveScale;
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
}
|
||||
|
||||
//player is walking in thick water
|
||||
if( !isDefined( self.inThickWater ) && !self inShallowWater( underwater, 32 ) )
|
||||
{
|
||||
self.inThickWater = true;
|
||||
self.moveSpeedScaler = .7*level.basePlayerMoveScale;
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
}
|
||||
|
||||
//player's eye is under water
|
||||
if ( !isDefined( self.underWater ) && !self isAboveWaterLine( underWater, 0 ) )
|
||||
{
|
||||
self.underWater = true;
|
||||
self thread playerHandleDamage();
|
||||
if( isAugmentedGameMode() )
|
||||
self DisableExo();
|
||||
if ( !self maps\mp\_utility::isUsingRemote() )
|
||||
{
|
||||
self shellShock( "underwater", 19 );
|
||||
self.isShocked = true;
|
||||
}
|
||||
|
||||
currentWeapon = self GetCurrentWeapon();
|
||||
if ( currentWeapon != "none" && WeaponInventoryType( currentWeapon ) == "primary" )
|
||||
{
|
||||
self.water_last_weapon = currentWeapon;
|
||||
}
|
||||
|
||||
if(IsDefined(level.gamemodeOnUnderWater))
|
||||
self [[level.gamemodeOnUnderWater]](underWater);
|
||||
self maps\mp\killstreaks\_coop_util::playerStopPromptForStreakSupport();
|
||||
}
|
||||
|
||||
//player's eye is underwater and player's in shallow water or prone or is not allowed to swim
|
||||
if ( isDefined( self.underWater ) && ( isDefined( self.isSwimming ) || !isDefined( self.isWading ) ) && ( self inShallowWater( underwater, level.swimming_depth ) || self GetStance() == "prone" || !level.allow_swimming ) )
|
||||
{
|
||||
self.isWading = true;
|
||||
self.isSwimming = undefined;
|
||||
self PlayerDisableUnderwater();
|
||||
if ( IsDefined( self.isJuggernaut ) && self.isJuggernaut == true )
|
||||
{
|
||||
self PlayerEnableUnderwater( "none" ); // Special case for when the player is using the Goliath Scorestreak.
|
||||
self AllowFire( false ); // Disable main weapons and lethal offhands. Does not include second offhand weapons.
|
||||
self DisableOffhandSecondaryWeapons();
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayerEnableUnderwater( "shallow" );
|
||||
}
|
||||
}
|
||||
|
||||
//player's eye is underwater and player's in deep water and the player is allowed to swim
|
||||
if ( isDefined( self.underWater ) && ( isDefined( self.isWading ) || !isDefined( self.isSwimming ) ) && ( !self inShallowWater( underwater, level.swimming_depth ) && self GetStance() != "prone" && level.allow_swimming ) )
|
||||
{
|
||||
self.isSwimming = true;
|
||||
self.isWading = undefined;
|
||||
self PlayerDisableUnderwater();
|
||||
if ( IsDefined( self.isJuggernaut ) && self.isJuggernaut == true )
|
||||
{
|
||||
self PlayerEnableUnderwater( "none" ); // Special case for when the player is using the Goliath Scorestreak.
|
||||
self AllowFire( false ); // Disable main weapons and lethal offhands. Does not include second offhand weapons.
|
||||
self DisableOffhandSecondaryWeapons();
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayerEnableUnderwater( "deep" );
|
||||
}
|
||||
}
|
||||
|
||||
//player's eye above the waterline
|
||||
if ( isDefined( self.underWater ) && self isAboveWaterLine( underWater, 0 ) )
|
||||
{
|
||||
self.underWater = undefined;
|
||||
self.isSwimming = undefined;
|
||||
self.isWading = undefined;
|
||||
self notify( "above_water" );
|
||||
speed = Distance( self GetVelocity(), ( 0, 0, 0 ) );
|
||||
point = (self.origin[0], self.origin[1], getWaterLine(underwater));
|
||||
playFX( level._effect[ "water_splash_emerge" ], point, AnglesToForward((0, self.angles[1], 0) + (270, 180, 0) ) );
|
||||
if ( !self maps\mp\_utility::isUsingRemote() )
|
||||
{
|
||||
self StopShellshock();
|
||||
self.isShocked = undefined;
|
||||
}
|
||||
self PlayerDisableUnderwater();
|
||||
if( isAugmentedGameMode() )
|
||||
self EnableExo();
|
||||
self maps\mp\killstreaks\_coop_util::playerStartPromptForStreakSupport();
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
IsActiveKillstreakWaterRestricted( player )//TODO: move this check into the killstreak functions
|
||||
{
|
||||
if ( IsDefined( player.killstreakIndexWeapon ) )
|
||||
{
|
||||
streakName = self.pers["killstreaks"][self.killstreakIndexWeapon].streakName;
|
||||
if ( IsDefined( streakName ) )
|
||||
{
|
||||
if( string_find( streakName, "turret" ) > 0 || string_find( streakName, "sentry" ) > 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
playerWaterClearWait()
|
||||
{
|
||||
msg = self waittill_any_return( "death", "out_of_water" );
|
||||
|
||||
self.underwaterMotionType = undefined;
|
||||
self.dont_give_or_take_weapon = undefined;
|
||||
self player_set_in_water(false);
|
||||
self.underWater = undefined;
|
||||
self.inThickWater = undefined;
|
||||
self.water_last_weapon = undefined;
|
||||
self.moveSpeedScaler = level.basePlayerMoveScale;
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
}
|
||||
|
||||
inWaterWake(underwater)
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "out_of_water" );
|
||||
|
||||
speed = Distance( self GetVelocity(), ( 0, 0, 0 ) );
|
||||
if (speed > 90)
|
||||
{
|
||||
point = (self.origin[0], self.origin[1], getWaterLine(underwater));
|
||||
playFX( level._effect[ "water_splash_enter" ], point, AnglesToForward((0, self.angles[1], 0) + (270, 180, 0) ) );
|
||||
}
|
||||
|
||||
while( true )
|
||||
{
|
||||
//loop fx based off of player speed
|
||||
velocity = self GetVelocity();
|
||||
speed = Distance( velocity, ( 0, 0, 0 ) );
|
||||
if (speed > 0)
|
||||
wait(max(( 1 - (speed / 120)),0.1) );
|
||||
else
|
||||
wait (0.3);
|
||||
|
||||
if (speed > 5)
|
||||
{
|
||||
movementDir = VectorNormalize((velocity[0], velocity[1], 0));
|
||||
forwardVec = AnglesToForward(VectorToAngles(movementDir) + (270, 180, 0));
|
||||
point = (self.origin[0], self.origin[1], getWaterLine(underwater)) + ((speed / 4) * movementDir);
|
||||
playFX( level._effect[ "water_wake" ], point, forwardVec );
|
||||
}
|
||||
else
|
||||
{
|
||||
point = (self.origin[0], self.origin[1], getWaterLine(underwater));
|
||||
playFX( level._effect[ "water_wake_stationary" ], point, AnglesToForward((0, self.angles[1], 0) + (270, 180, 0) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
playerhandleDamage()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "stopped_using_remote" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "above_water" );
|
||||
|
||||
self thread onPlayerDeath();
|
||||
|
||||
wait( 13 );
|
||||
|
||||
while( true )
|
||||
{
|
||||
if ( !IsDefined( self.isJuggernaut ) || self.isJuggernaut == false )
|
||||
{
|
||||
RadiusDamage( self.origin + anglesToForward( self.angles ) * 5, 1, 20, 20, undefined, "MOD_TRIGGER_HURT" );
|
||||
}
|
||||
|
||||
wait( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerDeath()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "above_water" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self player_set_in_water(false);
|
||||
self.underWater = undefined;
|
||||
self.inThickWater = undefined;
|
||||
self.isSwimming = undefined;
|
||||
self.isWading = undefined;
|
||||
self.water_last_weapon = undefined;
|
||||
self.underwaterMotionType = undefined;
|
||||
self.eye_velocity = 0;
|
||||
self.eyeHeightLastFrame = 0;
|
||||
self maps\mp\killstreaks\_coop_util::playerStartPromptForStreakSupport();
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//water check functions
|
||||
/////////////////////////
|
||||
|
||||
inShallowWater( trig, depth )
|
||||
{
|
||||
if( !isDefined( depth ) )
|
||||
depth = 32;
|
||||
if( level GetWaterLine( trig ) - self.origin[2] <= depth )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
isAboveWaterLine( trig, offset )
|
||||
{
|
||||
if( (self GetPlayerEyeHeight() + offset ) >= level GetWaterLine( trig ))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
GetPlayerEyeHeight()
|
||||
{
|
||||
player_eye = self geteye();
|
||||
self.eye_velocity = player_eye[2] - self.eyeHeightLastFrame;
|
||||
self.eyeHeightLastFrame = player_eye[2];
|
||||
return player_eye[2];
|
||||
}
|
||||
|
||||
GetWaterLine( trig )
|
||||
{
|
||||
water_line_struct = getstruct( trig.target, "targetname" );//TODO: assert if the struct is not defined!
|
||||
water_line = water_line_struct.origin[2];
|
||||
|
||||
return water_line;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
//SWIMMING
|
||||
////////////////////////
|
||||
|
||||
PlayerEnableUnderwater( type )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "end_swimming" );
|
||||
|
||||
if( !isDefined( type ) )
|
||||
type = "shallow";
|
||||
|
||||
/* Taking this out since it causes a bug where the player could switch weapons before the disableWeaponSwitch() call causing the player to get a weapon underwater;
|
||||
if( ( !self IsSwitchingWeapon() && ( type == "shallow" && CurrentWeapon == level.shallow_water_weapon ) || ( type == "deep" && CurrentWeapon == level.deep_water_weapon ) ) )
|
||||
type = "none";
|
||||
*/
|
||||
if( ( type == "shallow" && self HasWeapon( level.shallow_water_weapon ) ) || ( type == "deep" && self HasWeapon( level.deep_water_weapon ) ) )
|
||||
self.dont_give_or_take_weapon = true;
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case "deep":
|
||||
self give_water_weapon( level.deep_water_weapon );
|
||||
self SwitchToWeaponImmediate( level.deep_water_weapon );
|
||||
self.underwaterMotionType = "deep";
|
||||
break;
|
||||
case "shallow":
|
||||
self give_water_weapon( level.shallow_water_weapon );
|
||||
self SwitchToWeaponImmediate( level.shallow_water_weapon );
|
||||
self.underwaterMotionType = "shallow";
|
||||
break;
|
||||
case "none":
|
||||
self.underwaterMotionType = "none";
|
||||
break;
|
||||
default:
|
||||
self give_water_weapon( level.shallow_water_weapon );
|
||||
self SwitchToWeaponImmediate( level.shallow_water_weapon );
|
||||
self.underwaterMotionType = "shallow";
|
||||
break;
|
||||
}
|
||||
|
||||
self DisableWeaponPickup();
|
||||
self _disableWeaponSwitch();
|
||||
self _disableOffhandWeapons();
|
||||
}
|
||||
|
||||
playerDisableUnderwater()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
if( isDefined( self.underwaterMotionType ) )
|
||||
{
|
||||
type = self.underwaterMotionType;
|
||||
|
||||
self notify( "end_swimming" );
|
||||
|
||||
self EnableWeaponPickup();
|
||||
self _enableWeaponSwitch();
|
||||
self _enableOffhandWeapons();
|
||||
|
||||
if ( IsDefined( self.isJuggernaut ) && self.isJuggernaut == true && IsDefined( self.heavyExoData ) )
|
||||
{
|
||||
self AllowFire( true ); // Re-enable main weapon and lethal offhands (does not include secondary offhands).
|
||||
|
||||
// Disable lethals if the Goliath doesn't have the underbarrel rocket launcher module.
|
||||
if ( !IsDefined( self.heavyExoData.hasLongPunch ) || self.heavyExoData.hasLongPunch == false )
|
||||
{
|
||||
self DisableOffhandWeapons();
|
||||
}
|
||||
|
||||
// Disable tacticals if the Goliath doesn't have the swarm rockets module.
|
||||
if ( !IsDefined( self.heavyExoData.hasRockets ) || self.heavyExoData.hasRockets == false )
|
||||
{
|
||||
self DisableOffhandSecondaryWeapons();
|
||||
}
|
||||
else
|
||||
{
|
||||
self EnableOffhandSecondaryWeapons();
|
||||
}
|
||||
}
|
||||
|
||||
if( isDefined( self.water_last_weapon ) )
|
||||
{
|
||||
self switch_to_last_weapon( self.water_last_weapon );
|
||||
}
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case "deep":
|
||||
self take_water_weapon( level.deep_water_weapon );
|
||||
break;
|
||||
case "shallow":
|
||||
self take_water_weapon( level.shallow_water_weapon );
|
||||
break;
|
||||
case "none":
|
||||
break;
|
||||
default:
|
||||
self take_water_weapon( level.shallow_water_weapon );
|
||||
break;
|
||||
}
|
||||
|
||||
self.underwaterMotionType = undefined;
|
||||
self.dont_give_or_take_weapon = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
give_water_weapon( weapon )
|
||||
{
|
||||
if( !isDefined( self.dont_give_or_take_weapon ) || !self.dont_give_or_take_weapon )
|
||||
self giveWeapon( weapon );
|
||||
}
|
||||
|
||||
take_water_weapon( weapon )
|
||||
{
|
||||
if( !isDefined( self.dont_give_or_take_weapon ) || !self.dont_give_or_take_weapon )
|
||||
self takeWeapon( weapon );
|
||||
}
|
||||
|
||||
EnableExo()
|
||||
{
|
||||
self playerAllowHighJump(true);
|
||||
self playerAllowHighJumpDrop(true);
|
||||
self playerAllowBoostJump(true);
|
||||
self playerAllowPowerSlide(true);
|
||||
self playerAllowDodge(true);
|
||||
}
|
||||
|
||||
DisableExo()
|
||||
{
|
||||
self playerAllowHighJump(false);
|
||||
self playerAllowHighJumpDrop(false);
|
||||
self playerAllowBoostJump(false);
|
||||
self playerAllowPowerSlide(false);
|
||||
self playerAllowDodge(false);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: SetShallowWaterWeapon( weapon )"
|
||||
"Summary: forces the player to use this weapon when underwater in shallow water"
|
||||
"Module: Water"
|
||||
"MandatoryArg: <weapon> : the weapon to use when underwater in shallow water"
|
||||
"CallOn: Level"
|
||||
"Example: Level SetUnderWaterWeapon( "iw5_combatknife_mp" )"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
SetShallowWaterWeapon( weapon )
|
||||
{
|
||||
|
||||
assertEx( isValidUnderWaterWeapon( weapon ), "weapon is not a valid underwater weapon" );
|
||||
|
||||
level.shallow_water_weapon = weapon;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: SetDeepWaterWeapon( weapon )"
|
||||
"Summary: forces the player to use this weapon when underwater in deep water"
|
||||
"Module: Water"
|
||||
"MandatoryArg: <weapon> : the weapon to use when underwater in deep water"
|
||||
"CallOn: Level"
|
||||
"Example: Level SetUnderWaterWeapon( "iw5_underwater_mp" )"
|
||||
"SPMP: MP"
|
||||
///ScriptDocEnd
|
||||
=============
|
||||
*/
|
||||
|
||||
SetDeepWaterWeapon( weapon )
|
||||
{
|
||||
assertEx( isValidUnderWaterWeapon( weapon ), "weapon is not a valid underwater weapon" );
|
||||
|
||||
level.deep_water_weapon = weapon;
|
||||
}
|
||||
|
||||
isValidUnderWaterWeapon( weapon )
|
||||
{
|
||||
switch( weapon )
|
||||
{
|
||||
case "iw5_underwater_mp": //swim
|
||||
case "iw5_combatknife_mp": //knife
|
||||
case "none": //no weapon
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
399
raw/maps/mp/_zipline.gsc
Normal file
399
raw/maps/mp/_zipline.gsc
Normal file
@ -0,0 +1,399 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// prototype for generic zipline / rappel
|
||||
// - player can use their weapon while travelling on line
|
||||
// - player can drop off line by jumping
|
||||
|
||||
// SETUP:
|
||||
// - trigger_touch marks the area where the player will be prompted to use the zipline (suggest 32x32x32 trigger touching ground)
|
||||
// - script_origin marked as the target of the trigger_use_touch is the destination
|
||||
|
||||
init()
|
||||
{
|
||||
visuals = [];
|
||||
triggers = getentarray("zipline", "targetname");
|
||||
|
||||
for( i = 0; i < triggers.size; i++ )
|
||||
{
|
||||
zipline = maps\mp\gametypes\_gameobjects::createUseObject( "neutral", triggers[i], visuals, (0,0,0) );
|
||||
zipline maps\mp\gametypes\_gameobjects::allowUse( "any" );
|
||||
zipline maps\mp\gametypes\_gameobjects::setUseTime( 0.25 );
|
||||
zipline maps\mp\gametypes\_gameobjects::setUseText( &"MP_ZIPLINE_USE" );
|
||||
zipline maps\mp\gametypes\_gameobjects::setUseHintText( &"MP_ZIPLINE_USE" );
|
||||
zipline maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
|
||||
zipline.onBeginUse = ::onBeginUse;
|
||||
zipline.onUse = ::onUse;
|
||||
|
||||
targets = [];
|
||||
target = getEnt( triggers[i].target, "targetname");
|
||||
|
||||
if ( !isDefined( target ) )
|
||||
assertmsg( "No target found for zipline trigger located at: ( " + triggers[i].origin[0] + ", " + triggers[i].origin[1] + ", " + triggers[i].origin[2] + " )" );
|
||||
|
||||
while ( isDefined( target ) )
|
||||
{
|
||||
targets[targets.size] = target;
|
||||
if ( isDefined( target.target ) )
|
||||
target = getEnt( target.target, "targetname");
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
zipline.targets = targets;
|
||||
}
|
||||
|
||||
precacheModel( "tag_player" );
|
||||
|
||||
// elevator test
|
||||
init_elevator();
|
||||
}
|
||||
|
||||
|
||||
onBeginUse( player )
|
||||
{
|
||||
player playSound( "scrambler_pullout_lift_plr" );
|
||||
}
|
||||
|
||||
|
||||
onUse( player )
|
||||
{
|
||||
player thread zip( self );
|
||||
}
|
||||
|
||||
|
||||
zip( useObj )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "zipline_drop" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
// make the carrier
|
||||
carrier = spawn( "script_origin", useObj.trigger.origin );
|
||||
carrier.origin = useObj.trigger.origin;
|
||||
carrier.angles = self.angles;
|
||||
carrier setModel( "tag_player" );
|
||||
|
||||
// link the player
|
||||
self playerLinkToDelta( carrier, "tag_player", 1, 180, 180, 180, 180 );
|
||||
|
||||
// monitor player
|
||||
self thread watchDeath( carrier );
|
||||
self thread watchDrop( carrier );
|
||||
|
||||
// loop through the path of targets
|
||||
targets = useObj.targets;
|
||||
for( i=0; i < targets.size; i++ )
|
||||
{
|
||||
// time
|
||||
// JDS TODO: look into LDs specifying speed, accelleration, deceleration on the nodes
|
||||
time = distance( carrier.origin, targets[i].origin ) / 600;
|
||||
|
||||
// send it on its way
|
||||
acceleration = 0.0;
|
||||
if ( i==0 )
|
||||
acceleration = time*0.2;
|
||||
carrier moveTo( targets[i].origin, time, acceleration );
|
||||
if ( carrier.angles != targets[i].angles )
|
||||
carrier rotateTo( targets[i].angles, time*0.8 );
|
||||
|
||||
// wait
|
||||
wait( time );
|
||||
}
|
||||
|
||||
// all done
|
||||
self notify( "destination" );
|
||||
self unlink();
|
||||
carrier delete();
|
||||
}
|
||||
|
||||
|
||||
watchDrop( carrier )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "destination" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self notifyOnPlayerCommand( "zipline_drop", "+gostand" );
|
||||
|
||||
self waittill( "zipline_drop" );
|
||||
|
||||
self unlink();
|
||||
carrier delete();
|
||||
}
|
||||
|
||||
|
||||
watchDeath( carrier )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "destination" );
|
||||
self endon( "zipline_drop" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self unlink();
|
||||
carrier delete();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ELEVATOR TEST
|
||||
|
||||
// JDS TODO: make this more generic to handle multiple elevators in a level
|
||||
init_elevator()
|
||||
{
|
||||
visuals = [];
|
||||
triggers = getEntArray("elevator_button", "targetname");
|
||||
|
||||
level.elevator = spawnStruct();
|
||||
level.elevator.location = "floor1";
|
||||
level.elevator.states = [];
|
||||
level.elevator.states["elevator"] = "closed";
|
||||
level.elevator.destinations = [];
|
||||
|
||||
for( i = 0; i < triggers.size; i++ )
|
||||
{
|
||||
button = maps\mp\gametypes\_gameobjects::createUseObject( "neutral", triggers[i], visuals, (0,0,0) );
|
||||
button maps\mp\gametypes\_gameobjects::allowUse( "any" );
|
||||
button maps\mp\gametypes\_gameobjects::setUseTime( 0.25 );
|
||||
button maps\mp\gametypes\_gameobjects::setUseText( &"MP_ZIPLINE_USE" );
|
||||
button maps\mp\gametypes\_gameobjects::setUseHintText( &"MP_ZIPLINE_USE" );
|
||||
button maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
|
||||
button.onBeginUse = ::onBeginUse_elevator;
|
||||
button.onUse = ::onUse_elevator;
|
||||
|
||||
button.location = triggers[i].script_label;
|
||||
level.elevator.states[triggers[i].script_label] = "closed";
|
||||
|
||||
if ( isDefined( triggers[i].target ) )
|
||||
{
|
||||
destination = getStruct( triggers[i].target, "targetname" );
|
||||
if ( isDefined( destination ) )
|
||||
level.elevator.destinations[triggers[i].script_label] = destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onBeginUse_elevator( player )
|
||||
{
|
||||
// ding!
|
||||
}
|
||||
|
||||
// self = elevator button
|
||||
onUse_elevator( player )
|
||||
{
|
||||
switch( self.location )
|
||||
{
|
||||
case "floor1":
|
||||
{
|
||||
if ( level.elevator.states["floor1"] == "closed" )
|
||||
{
|
||||
if ( level.elevator.location == "floor1" )
|
||||
{
|
||||
if ( level.elevator.states["elevator"] == "closed" )
|
||||
{
|
||||
level thread open( "floor1" );
|
||||
level thread open( "elevator" );
|
||||
}
|
||||
}
|
||||
else if ( level.elevator.location == "floor2" )
|
||||
{
|
||||
if ( level.elevator.states["elevator"] == "opened" )
|
||||
{
|
||||
level notify( "stop_autoClose" );
|
||||
|
||||
level thread close( "floor2" );
|
||||
level close( "elevator" );
|
||||
}
|
||||
if ( level.elevator.states["elevator"] == "closed" )
|
||||
{
|
||||
level move();
|
||||
|
||||
level thread open( "floor1" );
|
||||
level thread open( "elevator" );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "floor2":
|
||||
{
|
||||
if ( level.elevator.states["floor2"] == "closed" )
|
||||
{
|
||||
if ( level.elevator.location == "floor2" )
|
||||
{
|
||||
if ( level.elevator.states["elevator"] == "closed" )
|
||||
{
|
||||
level thread open( "floor2" );
|
||||
level thread open( "elevator" );
|
||||
}
|
||||
}
|
||||
else if ( level.elevator.location == "floor1" )
|
||||
{
|
||||
if ( level.elevator.states["elevator"] == "opened" )
|
||||
{
|
||||
level notify( "stop_autoClose" );
|
||||
|
||||
level thread close( "floor1" );
|
||||
level close( "elevator" );
|
||||
}
|
||||
if ( level.elevator.states["elevator"] == "closed" )
|
||||
{
|
||||
level move();
|
||||
|
||||
level thread open( "floor2" );
|
||||
level thread open( "elevator" );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "elevator":
|
||||
{
|
||||
if ( level.elevator.states["elevator"] == "opened" )
|
||||
{
|
||||
level notify( "stop_autoClose" );
|
||||
|
||||
level thread close( level.elevator.location );
|
||||
level close( "elevator" );
|
||||
}
|
||||
if ( level.elevator.states["elevator"] == "closed" )
|
||||
{
|
||||
level move();
|
||||
|
||||
level thread open( level.elevator.location );
|
||||
level thread open( "elevator" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open( label )
|
||||
{
|
||||
level.elevator.states[label] = "opening";
|
||||
|
||||
doorL = getEnt( "e_door_" + label + "_left", "targetname" );
|
||||
doorR = getEnt( "e_door_" + label + "_right", "targetname" );
|
||||
|
||||
//Changing script to accommodate both mp_fahrenheit and mp_vault - Mary
|
||||
|
||||
if ( isDefined ( doorL.script_noteworthy ) && doorL.script_noteworthy == "fahrenheit" )
|
||||
{
|
||||
//Mary door move
|
||||
doorL moveTo( doorL.origin - anglesToForward( doorL.angles ) * 35, 2 );
|
||||
doorR moveTo( doorR.origin + anglesToForward( doorR.angles ) * 35, 2 );
|
||||
|
||||
doorL PlaySound( "elev_door_open" );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Sahas door move
|
||||
doorL moveTo( doorL.origin - anglesToRight( doorL.angles ) * 35, 2 );
|
||||
doorR moveTo( doorR.origin + anglesToRight( doorR.angles ) * 35, 2 );
|
||||
}
|
||||
|
||||
wait( 2 );
|
||||
|
||||
level.elevator.states[label] = "opened";
|
||||
|
||||
if ( label == "elevator" )
|
||||
level thread autoClose();
|
||||
}
|
||||
|
||||
|
||||
close( label )
|
||||
{
|
||||
level.elevator.states[label] = "closing";
|
||||
|
||||
doorL = getEnt( "e_door_" + label + "_left", "targetname" );
|
||||
doorR = getEnt( "e_door_" + label + "_right", "targetname" );
|
||||
|
||||
if ( isDefined ( doorL.script_noteworthy ) && doorL.script_noteworthy == "fahrenheit" )
|
||||
{
|
||||
//Mary door move
|
||||
doorL moveTo( doorL.origin + anglesToForward( doorL.angles ) * 35, 2 );
|
||||
doorR moveTo( doorR.origin - anglesToForward( doorR.angles ) * 35, 2 );
|
||||
|
||||
doorL PlaySound ( "elev_door_close" );
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//Sahas door move
|
||||
doorL moveTo( doorL.origin + anglesToRight( doorL.angles ) * 35, 2 );
|
||||
doorR moveTo( doorR.origin - anglesToRight( doorR.angles ) * 35, 2 );
|
||||
}
|
||||
|
||||
wait( 2 );
|
||||
|
||||
level.elevator.states[label] = "closed";
|
||||
}
|
||||
|
||||
|
||||
autoClose()
|
||||
{
|
||||
level endon( "stop_autoClose" );
|
||||
|
||||
wait( 10 );
|
||||
|
||||
level thread close( level.elevator.location );
|
||||
level thread close( "elevator" );
|
||||
}
|
||||
|
||||
|
||||
// JDS TODO: get nodes for doors or link them (get rid of delta calcs)
|
||||
move()
|
||||
{
|
||||
level.elevator.states["elevator"] = "moving";
|
||||
|
||||
doorL = getEnt( "e_door_elevator_left", "targetname" );
|
||||
doorR = getEnt( "e_door_elevator_right", "targetname" );
|
||||
elevator = getEnt( "elevator", "targetname" );
|
||||
// elevator_models = getEntArray( "elevator_models", "targetname");
|
||||
|
||||
// Elevator now supports script_models that move with it
|
||||
// foreach( emodels_piece in elevator_models )
|
||||
// {
|
||||
// emodels_piece linkto( elevator );
|
||||
// }
|
||||
|
||||
if ( level.elevator.location == "floor1" )
|
||||
{
|
||||
level.elevator.location = "floor2";
|
||||
|
||||
delta = doorL.origin[2] - level.elevator.destinations["floor1"].origin[2];
|
||||
doorL moveTo( (doorL.origin[0], doorL.origin[1], level.elevator.destinations["floor2"].origin[2]+delta), 5 );
|
||||
|
||||
delta = doorR.origin[2] - level.elevator.destinations["floor1"].origin[2];
|
||||
doorR moveTo( (doorR.origin[0], doorR.origin[1], level.elevator.destinations["floor2"].origin[2]+delta), 5 );
|
||||
|
||||
elevator moveTo( level.elevator.destinations["floor2"].origin, 5 );
|
||||
}
|
||||
else
|
||||
{
|
||||
level.elevator.location = "floor1";
|
||||
|
||||
delta = doorL.origin[2] - level.elevator.destinations["floor2"].origin[2];
|
||||
doorL moveTo( (doorL.origin[0], doorL.origin[1], level.elevator.destinations["floor1"].origin[2]+delta), 5 );
|
||||
|
||||
delta = doorR.origin[2] - level.elevator.destinations["floor2"].origin[2];
|
||||
doorR moveTo( (doorR.origin[0], doorR.origin[1], level.elevator.destinations["floor1"].origin[2]+delta), 5 );
|
||||
|
||||
elevator moveTo( level.elevator.destinations["floor1"].origin, 5 );
|
||||
}
|
||||
|
||||
wait( 5 );
|
||||
|
||||
//Added elevator bell ding (doesn't work correctly with onBeginUse_elevator) - Mary
|
||||
elevator PlaySound ( "elev_bell_ding" );
|
||||
|
||||
level.elevator.states["elevator"] = "closed";
|
||||
}
|
129
raw/maps/mp/agents/_agent_common.gsc
Normal file
129
raw/maps/mp/agents/_agent_common.gsc
Normal file
@ -0,0 +1,129 @@
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentAdded
|
||||
//=======================================================
|
||||
CodeCallback_AgentAdded()
|
||||
{
|
||||
self initAgentScriptVariables();
|
||||
|
||||
agentTeam = "axis";
|
||||
|
||||
if( (level.numagents % 2) == 0 )
|
||||
{
|
||||
agentTeam = "allies";
|
||||
}
|
||||
|
||||
level.numagents++;
|
||||
self set_agent_team( agentTeam );
|
||||
|
||||
level.agentArray[ level.agentArray.size ] = self;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentDamaged
|
||||
//=======================================================
|
||||
CodeCallback_AgentDamaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
eAttacker = _validateAttacker( eAttacker );
|
||||
|
||||
self [[ self agentFunc( "on_damaged" ) ]]( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentKilled
|
||||
//=======================================================
|
||||
CodeCallback_AgentKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
eAttacker = _validateAttacker( eAttacker );
|
||||
|
||||
self thread [[ self agentFunc("on_killed") ]](eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration);
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// init
|
||||
//========================================================
|
||||
init()
|
||||
{
|
||||
initAgentLevelVariables();
|
||||
|
||||
// add all the agents we're supposed to have in the game with us
|
||||
level thread add_agents_to_game();
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// connectNewAgent
|
||||
//=======================================================
|
||||
connectNewAgent( agent_type, team, class )
|
||||
{
|
||||
agent = getFreeAgent( agent_type );
|
||||
|
||||
if ( IsDefined( agent ) )
|
||||
{
|
||||
agent.connectTime = GetTime();
|
||||
|
||||
if ( IsDefined( team ) )
|
||||
agent set_agent_team( team );
|
||||
else
|
||||
agent set_agent_team( agent.team );
|
||||
|
||||
if ( IsDefined( class ) )
|
||||
agent.class_override = class;
|
||||
|
||||
if( IsDefined(level.agent_funcs[agent_type]["onAIConnect"]) )
|
||||
agent [[ agent agentFunc("onAIConnect") ]]();
|
||||
|
||||
agent maps\mp\gametypes\_spawnlogic::addToCharactersArray();
|
||||
|
||||
AssertEx(agent.connectTime == GetTime(), "Agent spawn took too long - there should be no waits in connectNewAgent");
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// initAgentLevelVariables
|
||||
//========================================================
|
||||
initAgentLevelVariables()
|
||||
{
|
||||
level.agentArray = [];
|
||||
level.numagents = 0;
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// add_agents_to_game
|
||||
//========================================================
|
||||
add_agents_to_game()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level waittill("connected", player);
|
||||
|
||||
maxagents = GetMaxAgents();
|
||||
|
||||
while( level.agentArray.size < maxagents )
|
||||
{
|
||||
agent = AddAgent();
|
||||
|
||||
if( !IsDefined( agent) )
|
||||
{
|
||||
waitframe();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// set_agent_health
|
||||
//========================================================
|
||||
set_agent_health( health )
|
||||
{
|
||||
self.agenthealth = health;
|
||||
self.health = health;
|
||||
self.maxhealth = health;
|
||||
}
|
458
raw/maps/mp/agents/_agent_utility.gsc
Normal file
458
raw/maps/mp/agents/_agent_utility.gsc
Normal file
@ -0,0 +1,458 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
//========================================================
|
||||
// agentFunc
|
||||
//========================================================
|
||||
agentFunc( func_name )
|
||||
{
|
||||
assert( IsAgent( self ) );
|
||||
assert( IsDefined(func_name) );
|
||||
assert( isDefined(self.agent_type) );
|
||||
assert( isDefined(level.agent_funcs[self.agent_type]) );
|
||||
assert( isDefined(level.agent_funcs[self.agent_type][func_name]) );
|
||||
|
||||
return level.agent_funcs[self.agent_type][func_name];
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// set_agent_team
|
||||
//========================================================
|
||||
set_agent_team( team, optional_owner )
|
||||
{
|
||||
// since an agent entity has both a "sentient" and an "agent", we need both
|
||||
// these to understand the team the entity is on (much as client entities
|
||||
// have a "sentient" and a "client"). The "team" field sets the "sentient"
|
||||
// team and the "agentteam" field sets the "agent" team.
|
||||
self.team = team;
|
||||
self.agentteam = team;
|
||||
self.pers["team"] = team;
|
||||
|
||||
self.owner = optional_owner;
|
||||
self SetOtherEnt( optional_owner );
|
||||
self SetEntityOwner( optional_owner );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// initAgentScriptVariables
|
||||
//=======================================================
|
||||
initAgentScriptVariables()
|
||||
{
|
||||
self.agent_type = "player"; // TODO: communicate this to code?
|
||||
self.pers = [];
|
||||
self.hasDied = false;
|
||||
self.isActive = false;
|
||||
self.isAgent = true;
|
||||
self.wasTI = false;
|
||||
self.isSniper = false;
|
||||
self.spawnTime = 0;
|
||||
self.entity_number = self GetEntityNumber();
|
||||
self.agent_teamParticipant = false;
|
||||
self.agent_gameParticipant = false;
|
||||
self.canPerformClientTraces = false;
|
||||
self.agentname = undefined;
|
||||
self.ignoreall = false;
|
||||
self.ignoreme = false;
|
||||
|
||||
self DetachAll();
|
||||
|
||||
self initPlayerScriptVariables( false );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// initPlayerScriptVariables
|
||||
//========================================================
|
||||
initPlayerScriptVariables( asPlayer )
|
||||
{
|
||||
if ( !asPlayer )
|
||||
{
|
||||
// Not as a player
|
||||
self.class = undefined;
|
||||
self.lastClass = undefined;
|
||||
self.moveSpeedScaler = undefined;
|
||||
self.avoidKillstreakOnSpawnTimer = undefined;
|
||||
self.guid = undefined;
|
||||
self.name = undefined;
|
||||
self.saved_actionSlotData = undefined;
|
||||
self.perks = undefined;
|
||||
self.weaponList = undefined;
|
||||
self.omaClassChanged = undefined;
|
||||
self.objectiveScaler = undefined;
|
||||
self.touchTriggers = undefined;
|
||||
self.carryObject = undefined;
|
||||
self.claimTrigger = undefined;
|
||||
self.canPickupObject = undefined;
|
||||
self.killedInUse = undefined;
|
||||
self.sessionteam = undefined;
|
||||
self.sessionstate = undefined;
|
||||
self.lastSpawnTime = undefined;
|
||||
self.lastspawnpoint = undefined;
|
||||
self.disabledWeapon = undefined;
|
||||
self.disabledWeaponSwitch = undefined;
|
||||
self.disabledOffhandWeapons = undefined;
|
||||
self.disabledUsability = undefined;
|
||||
self.shieldDamage = undefined;
|
||||
self.shieldBulletHits = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
// As a player
|
||||
self.moveSpeedScaler = level.basePlayerMoveScale;
|
||||
self.avoidKillstreakOnSpawnTimer = 5;
|
||||
self.guid = self getUniqueId();
|
||||
self.name = self.guid;
|
||||
self.sessionteam = self.team;
|
||||
self.sessionstate = "playing";
|
||||
self.shieldDamage = 0;
|
||||
self.shieldBulletHits = 0;
|
||||
self.agent_gameParticipant = true; // If initialized as a player, always make agent a game participant
|
||||
|
||||
self maps\mp\gametypes\_playerlogic::setupSavedActionSlots();
|
||||
self thread maps\mp\perks\_perks::onPlayerSpawned();
|
||||
|
||||
if ( IsGameParticipant( self ) )
|
||||
{
|
||||
self.objectiveScaler = 1;
|
||||
self maps\mp\gametypes\_gameobjects::init_player_gameobjects();
|
||||
self.disabledWeapon = 0;
|
||||
self.disabledWeaponSwitch = 0;
|
||||
self.disabledOffhandWeapons = 0;
|
||||
}
|
||||
}
|
||||
|
||||
self.disabledUsability = 1;
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// getFreeAgent
|
||||
//===========================================
|
||||
getFreeAgent( agent_type )
|
||||
{
|
||||
freeAgent = undefined;
|
||||
|
||||
if( IsDefined( level.agentArray ) )
|
||||
{
|
||||
foreach( agent in level.agentArray )
|
||||
{
|
||||
if( ( !IsDefined( agent.isActive ) || !agent.isActive ) && ( !IsDefined( agent.isReserved ) || !agent.isReserved ) )
|
||||
{
|
||||
if ( IsDefined(agent.waitingToDeactivate) && agent.waitingToDeactivate )
|
||||
continue;
|
||||
|
||||
if ( IsDefined(level.despawning_agents) && array_contains(level.despawning_agents,agent) )
|
||||
continue;
|
||||
|
||||
freeAgent = agent;
|
||||
|
||||
freeAgent initAgentScriptVariables();
|
||||
|
||||
if ( IsDefined( agent_type ) )
|
||||
freeAgent.agent_type = agent_type; // TODO: communicate this to code?
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return freeAgent;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// activateAgent
|
||||
//=======================================================
|
||||
activateAgent()
|
||||
{
|
||||
/#
|
||||
if ( !self.isActive )
|
||||
{
|
||||
// Activating this agent, ensure that he has connected on the same frame
|
||||
AssertEx(self.connectTime == GetTime(), "Agent spawn took too long - there should be no waits in between connectNewAgent and spawning the agent");
|
||||
}
|
||||
#/
|
||||
self.isActive = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======================================================
|
||||
// deactivateAgent
|
||||
//=======================================================
|
||||
deactivateAgent()
|
||||
{
|
||||
self thread deactivateAgentDelayed();
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// deactivateAgentDelayed
|
||||
//=======================================================
|
||||
deactivateAgentDelayed()
|
||||
{
|
||||
self notify("deactivateAgentDelayed");
|
||||
self endon("deactivateAgentDelayed");
|
||||
|
||||
if ( !IsDefined(level.despawning_agents) )
|
||||
level.despawning_agents = [];
|
||||
|
||||
if ( !array_contains(level.despawning_agents,self) )
|
||||
level.despawning_agents = array_add(level.despawning_agents,self);
|
||||
|
||||
// During the 0.05s wait in deactivateAgentDelayed, the agent's script variables are all cleared out
|
||||
// So we need to do this now while IsGameParticipant can still be checked
|
||||
if ( IsGameParticipant(self) )
|
||||
self maps\mp\gametypes\_spawnlogic::removeFromParticipantsArray();
|
||||
|
||||
self maps\mp\gametypes\_spawnlogic::removeFromCharactersArray();
|
||||
|
||||
//println("** " + GetTime() + " About to clear agent script vars (1) for agent " + self GetEntityNumber() + " of type " + self.agent_type );
|
||||
|
||||
// Wait till next frame before we "disconnect"
|
||||
// That way things waiting on "death" but have endon("disconnect") will still function
|
||||
// e.g. maps\mp\killstreaks\_juggernaut::juggRemover()
|
||||
wait 0.05;
|
||||
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
self.owner = undefined;
|
||||
self.connectTime = undefined;
|
||||
self.waitingToDeactivate = undefined;
|
||||
|
||||
// Clear this agent from any other character's attackers array
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
if ( IsDefined( character.attackers ) )
|
||||
{
|
||||
foreach ( index, attacker in character.attackers )
|
||||
{
|
||||
if ( attacker == self )
|
||||
character.attackers[index] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.headModel = undefined;
|
||||
self DetachAll();
|
||||
|
||||
self notify("disconnect");
|
||||
|
||||
//println("** " + GetTime() + " About to clear agent script vars (2) for agent " + self GetEntityNumber() );
|
||||
self AgentClearScriptVars(); // This needs to be the last line, anything after this won't execute correctly because the entity has been cleared
|
||||
|
||||
level.despawning_agents = array_remove(level.despawning_agents,self);
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getNumActiveAgents
|
||||
//===========================================
|
||||
getNumActiveAgents( type )
|
||||
{
|
||||
if ( !IsDefined(type) )
|
||||
type = "all";
|
||||
|
||||
agents = getActiveAgentsOfType(type);
|
||||
return agents.size;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getActiveAgentsOfType
|
||||
//===========================================
|
||||
getActiveAgentsOfType( type )
|
||||
{
|
||||
Assert(IsDefined(type));
|
||||
agents = [];
|
||||
|
||||
if ( !IsDefined( level.agentArray ) )
|
||||
return agents;
|
||||
|
||||
foreach ( agent in level.agentArray )
|
||||
{
|
||||
if ( IsDefined( agent.isActive ) && agent.isActive )
|
||||
{
|
||||
if ( type == "all" || agent.agent_type == type )
|
||||
agents[agents.size] = agent;
|
||||
}
|
||||
}
|
||||
|
||||
return agents;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getNumOwnedActiveAgents
|
||||
//===========================================
|
||||
getNumOwnedActiveAgents( player )
|
||||
{
|
||||
return getNumOwnedActiveAgentsByType( player, "all" );
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// getNumOwnedActiveAgentsByType
|
||||
//===========================================
|
||||
getNumOwnedActiveAgentsByType( player, type )
|
||||
{
|
||||
Assert(IsDefined(type));
|
||||
numOwnedActiveAgents = 0;
|
||||
|
||||
if( !IsDefined(level.agentArray) )
|
||||
{
|
||||
return numOwnedActiveAgents;
|
||||
}
|
||||
|
||||
foreach( agent in level.agentArray )
|
||||
{
|
||||
if( IsDefined( agent.isActive ) && agent.isActive )
|
||||
{
|
||||
if ( IsDefined(agent.owner) && (agent.owner == player) )
|
||||
{
|
||||
if ( type == "all" || agent.agent_type == type )
|
||||
numOwnedActiveAgents++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numOwnedActiveAgents;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// getValidSpawnPathNodeNearPlayer
|
||||
//=======================================================
|
||||
getValidSpawnPathNodeNearPlayer( bDoPhysicsTraceToPlayer, bDoPhysicsTraceToValidateNode ) // self = player
|
||||
{
|
||||
assert( isPlayer( self ) );
|
||||
|
||||
nodeArray = GetNodesInRadius( self.origin, 350, 64, 128, "Path" );
|
||||
|
||||
if( !IsDefined(nodeArray) || (nodeArray.size == 0) )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ( IsDefined(level.waterDeleteZ) && IsDefined(level.trigUnderWater) )
|
||||
{
|
||||
// Ignore any nodes where the agent would die immediately upon spawning
|
||||
nodeArrayOld = nodeArray;
|
||||
nodeArray = [];
|
||||
foreach( node in nodeArrayOld )
|
||||
{
|
||||
if ( node.origin[ 2 ] > level.waterDeleteZ || !IsPointInVolume( node.origin, level.trigUnderWater ) )
|
||||
nodeArray[nodeArray.size] = node;
|
||||
}
|
||||
}
|
||||
|
||||
playerDirection = AnglesToForward( self.angles );
|
||||
bestDot = -10;
|
||||
|
||||
playerHeight = maps\mp\gametypes\_spawnlogic::getPlayerTraceHeight( self );
|
||||
zOffset = ( 0, 0, playerHeight );
|
||||
|
||||
if ( !IsDefined(bDoPhysicsTraceToPlayer) )
|
||||
bDoPhysicsTraceToPlayer = false;
|
||||
|
||||
if ( !IsDefined(bDoPhysicsTraceToValidateNode) )
|
||||
bDoPhysicsTraceToValidateNode = false;
|
||||
|
||||
pathNodeSortedByDot = [];
|
||||
pathNodeDotValues = [];
|
||||
foreach( pathNode in nodeArray )
|
||||
{
|
||||
if ( !pathNode DoesNodeAllowStance("stand") )
|
||||
continue;
|
||||
|
||||
directionToNode = VectorNormalize( pathNode.origin - self.origin );
|
||||
dot = VectorDot( playerDirection, directionToNode );
|
||||
|
||||
i = 0;
|
||||
for ( ; i < pathNodeDotValues.size; i++ )
|
||||
{
|
||||
if ( dot > pathNodeDotValues[i] )
|
||||
{
|
||||
for ( j = pathNodeDotValues.size; j > i; j-- )
|
||||
{
|
||||
pathNodeDotValues[j] = pathNodeDotValues[j-1];
|
||||
pathNodeSortedByDot[j] = pathNodeSortedByDot[j-1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pathNodeSortedByDot[i] = pathNode;
|
||||
pathNodeDotValues[i] = dot;
|
||||
}
|
||||
|
||||
// pick a path node in the player's view
|
||||
for ( i = 0; i < pathNodeSortedByDot.size; i++ )
|
||||
{
|
||||
pathNode = pathNodeSortedByDot[i];
|
||||
|
||||
traceStart = self.origin + zOffset;
|
||||
traceEnd = pathNode.origin + zOffset;
|
||||
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
// prevent selecting a node that the player cannot see
|
||||
if( !SightTracePassed( traceStart, traceEnd, false, self ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bDoPhysicsTraceToValidateNode )
|
||||
{
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
hitPos = PlayerPhysicsTrace( pathNode.origin + zOffset, pathNode.origin );
|
||||
if ( DistanceSquared( hitPos, pathNode.origin ) > 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bDoPhysicsTraceToPlayer )
|
||||
{
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
hitPos = PhysicsTrace( traceStart, traceEnd );
|
||||
if ( DistanceSquared( hitPos, traceEnd ) > 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
return pathNode;
|
||||
}
|
||||
|
||||
// always return a node for safeguard
|
||||
if( (pathNodeSortedByDot.size > 0) && IsDefined(level.isHorde) )
|
||||
return pathNodeSortedByDot[0];
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// killAgent
|
||||
//=======================================================
|
||||
killAgent( agent )
|
||||
{
|
||||
// do enough damage to kill the agent regardless of any damage mitigation
|
||||
agent DoDamage( agent.health + 500000, agent.origin );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// killDog
|
||||
//=======================================================
|
||||
killDog() // self == dog
|
||||
{
|
||||
self [[ self agentFunc( "on_damaged" ) ]](
|
||||
level, // eInflictor The entity that causes the damage.(e.g. a turret)
|
||||
undefined, // eAttacker The entity that is attacking.
|
||||
self.health + 1, // iDamage Integer specifying the amount of damage done
|
||||
0, // iDFlags Integer specifying flags that are to be applied to the damage
|
||||
"MOD_CRUSH", // sMeansOfDeath Integer specifying the method of death
|
||||
"none", // sWeapon The weapon number of the weapon used to inflict the damage
|
||||
( 0, 0, 0 ), // vPoint The point the damage is from?
|
||||
(0, 0, 0), // vDir The direction of the damage
|
||||
"none", // sHitLoc The location of the hit
|
||||
0 // psOffsetTime The time offset for the damage
|
||||
);
|
||||
}
|
557
raw/maps/mp/agents/_agents.gsc
Normal file
557
raw/maps/mp/agents/_agents.gsc
Normal file
@ -0,0 +1,557 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
//=======================================================================
|
||||
// main
|
||||
// This is functions is called directly from native code on game startup
|
||||
// The particular gametype's main() is called from native code afterward
|
||||
//=======================================================================
|
||||
main()
|
||||
{
|
||||
if( IsDefined( level.createFX_enabled ) && level.createFX_enabled )
|
||||
return;
|
||||
|
||||
setup_callbacks();
|
||||
|
||||
// Enable badplaces in destructibles
|
||||
level.badplace_cylinder_func = ::badplace_cylinder;
|
||||
level.badplace_delete_func = ::badplace_delete;
|
||||
|
||||
/#
|
||||
level thread monitor_scr_agent_players();
|
||||
#/
|
||||
|
||||
level thread maps\mp\agents\_agent_common::init();
|
||||
if ( !InVirtualLobby() )
|
||||
{
|
||||
level thread maps\mp\killstreaks\_agent_killstreak::init();
|
||||
level thread maps\mp\killstreaks\_dog_killstreak::init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// setup_callbacks
|
||||
//========================================================
|
||||
setup_callbacks()
|
||||
{
|
||||
if ( !IsDefined( level.agent_funcs ) )
|
||||
level.agent_funcs = [];
|
||||
|
||||
level.agent_funcs["player"] = [];
|
||||
|
||||
level.agent_funcs["player"]["spawn"] = ::spawn_agent_player;
|
||||
level.agent_funcs["player"]["think"] = maps\mp\bots\_bots_gametype_war::bot_war_think;
|
||||
level.agent_funcs["player"]["on_killed"] = ::on_agent_player_killed;
|
||||
level.agent_funcs["player"]["on_damaged"] = ::on_agent_player_damaged;
|
||||
level.agent_funcs["player"]["on_damaged_finished"] = ::agent_damage_finished;
|
||||
|
||||
maps\mp\killstreaks\_agent_killstreak::setup_callbacks();
|
||||
maps\mp\killstreaks\_dog_killstreak::setup_callbacks();
|
||||
}
|
||||
|
||||
wait_till_agent_funcs_defined()
|
||||
{
|
||||
while( !IsDefined(level.agent_funcs) )
|
||||
wait(0.05);
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
//=======================================================
|
||||
// new_scr_agent_team
|
||||
//=======================================================
|
||||
new_scr_agent_team()
|
||||
{
|
||||
teamCounts = [];
|
||||
teamCounts["allies"] = 0;
|
||||
teamCounts["axis"] = 0;
|
||||
minTeam = undefined;
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if ( !IsDefined( teamCounts[player.team] ) )
|
||||
teamCounts[player.team] = 0;
|
||||
if ( IsTeamParticipant( player ) )
|
||||
teamCounts[player.team]++;
|
||||
}
|
||||
foreach ( team, count in teamCounts )
|
||||
{
|
||||
if ( (team != "spectator") && (!IsDefined(minTeam) || teamCounts[minTeam] > count) )
|
||||
minTeam = team;
|
||||
}
|
||||
|
||||
return minTeam;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// monitor_scr_agent_players
|
||||
//=======================================================
|
||||
monitor_scr_agent_players()
|
||||
{
|
||||
SetDevDvarIfUninitialized( "scr_agent_players_add", "0" );
|
||||
SetDevDvarIfUninitialized( "scr_agent_players_drop", "0" );
|
||||
|
||||
while(level.players.size == 0)
|
||||
wait(0.05); // Agents don't exist until a player connects
|
||||
|
||||
while(!IsDefined(level.bot_loadouts_initialized))
|
||||
wait(0.05); // Agents need to wait until bot loadouts have been initialized
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait(0.1);
|
||||
|
||||
add_agent_players = getdvarInt("scr_agent_players_add");
|
||||
drop_agent_players = getdvarInt("scr_agent_players_drop");
|
||||
|
||||
if ( add_agent_players != 0 )
|
||||
SetDevDvar( "scr_agent_players_add", 0 );
|
||||
|
||||
if ( drop_agent_players != 0 )
|
||||
SetDevDvar( "scr_agent_players_drop", 0 );
|
||||
|
||||
for ( i = 0; i < add_agent_players; i++ )
|
||||
{
|
||||
agent = add_humanoid_agent( "player", new_scr_agent_team(), undefined, undefined, undefined, undefined, true, true, "follow_code_and_dev_dvar" );
|
||||
if ( IsDefined( agent ) )
|
||||
agent.agent_teamParticipant = true;
|
||||
}
|
||||
|
||||
foreach ( agent in level.agentArray )
|
||||
{
|
||||
if ( !IsDefined( agent.isActive ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( agent.isActive ) && agent.isActive && agent.agent_type == "player" )
|
||||
{
|
||||
if ( drop_agent_players > 0 )
|
||||
{
|
||||
agent.respawn_on_death = undefined;
|
||||
agent Suicide();
|
||||
Assert(agent.health == 0); // Ensure that Suicide worked
|
||||
drop_agent_players--;
|
||||
wait(0.1);
|
||||
Assert(!IsDefined(agent.isActive) || !agent.isActive); // Agent should have been deactivated already
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
//=======================================================
|
||||
// add_humanoid_agent
|
||||
//=======================================================
|
||||
add_humanoid_agent( agent_type, team, class, optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( agent_type, team, class );
|
||||
|
||||
if( IsDefined( agent ) )
|
||||
{
|
||||
agent thread [[ agent agentFunc("spawn") ]]( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty );
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// spawn_agent_player
|
||||
//========================================================
|
||||
spawn_agent_player( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while( !IsDefined(level.getSpawnPoint) )
|
||||
{
|
||||
waitframe();
|
||||
}
|
||||
|
||||
if( self.hasDied )
|
||||
{
|
||||
wait( RandomIntRange(6, 10) );
|
||||
}
|
||||
|
||||
self initPlayerScriptVariables( true );
|
||||
|
||||
// allow killstreaks to pass in specific spawn locations
|
||||
if( IsDefined(optional_spawnOrigin) && IsDefined(optional_spawnAngles) )
|
||||
{
|
||||
spawnOrigin = optional_spawnOrigin;
|
||||
spawnAngles = optional_spawnAngles;
|
||||
|
||||
self.lastSpawnPoint = SpawnStruct();
|
||||
self.lastSpawnPoint.origin = spawnOrigin;
|
||||
self.lastSpawnPoint.angles = spawnAngles;
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
|
||||
// Player specific variables needed in damage processing
|
||||
self.lastSpawnPoint = spawnpoint;
|
||||
}
|
||||
self activateAgent();
|
||||
self.lastSpawnTime = GetTime();
|
||||
self.spawnTime = GetTime();
|
||||
|
||||
phys_trace_start = spawnOrigin + (0,0,25);
|
||||
phys_trace_end = spawnOrigin;
|
||||
newSpawnOrigin = PlayerPhysicsTrace(phys_trace_start, phys_trace_end);
|
||||
if ( DistanceSquared( newSpawnOrigin, phys_trace_start ) > 1 )
|
||||
{
|
||||
// If the result from the physics trace wasn't immediately in solid, then use it instead
|
||||
spawnOrigin = newSpawnOrigin;
|
||||
}
|
||||
|
||||
// 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 );
|
||||
|
||||
if ( IsDefined( difficulty ) )
|
||||
self.agent_override_difficulty = difficulty;
|
||||
|
||||
if ( IsDefined( self.agent_override_difficulty ) )
|
||||
{
|
||||
if ( self.agent_override_difficulty == "follow_code_and_dev_dvar" )
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( self BotGetDifficulty(), true );
|
||||
else
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( difficulty );
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( self BotGetDifficulty() );
|
||||
}
|
||||
|
||||
if ( IsDefined(use_randomized_personality) && use_randomized_personality )
|
||||
self.use_randomized_personality = true;
|
||||
|
||||
if ( IsDefined ( self.use_randomized_personality) && self.use_randomized_personality )
|
||||
{
|
||||
if ( !self.hasDied )
|
||||
{
|
||||
// If this is the first time this agent has spawned, balance personalities unless we are restricting them based on difficulty
|
||||
allowAdvPersonality = self BotGetDifficultySetting( "advancedPersonality" );
|
||||
if ( IsDefined( allowAdvPersonality ) && allowAdvPersonality != 0 )
|
||||
self maps\mp\bots\_bots_personality::bot_balance_personality();
|
||||
}
|
||||
|
||||
/#
|
||||
self maps\mp\bots\_bots::bot_set_personality_from_dev_dvar();
|
||||
#/
|
||||
self maps\mp\bots\_bots_personality::bot_assign_personality_functions(); // Randomized personality was already set, so just need to setup functions
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\bots\_bots_util::bot_set_personality( "default" );
|
||||
}
|
||||
|
||||
self initPlayerClass();
|
||||
|
||||
self maps\mp\agents\_agent_common::set_agent_health( 100 );
|
||||
if ( IsDefined(respawn_on_death) && respawn_on_death )
|
||||
self.respawn_on_death = true;
|
||||
|
||||
// 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 );
|
||||
|
||||
if( isDefined( self.owner ) )
|
||||
self thread destroyOnOwnerDisconnect( self.owner );
|
||||
|
||||
self thread maps\mp\_flashgrenades::monitorFlash();
|
||||
|
||||
// switch to agent bot mode and wipe all AI info clean
|
||||
self EnableAnimState( false );
|
||||
|
||||
self [[level.onSpawnPlayer]]();
|
||||
self maps\mp\gametypes\_class::giveAndApplyLoadout( self.team, self.class, true );
|
||||
|
||||
self thread maps\mp\bots\_bots::bot_think_watch_enemy( true );
|
||||
self thread maps\mp\bots\_bots::bot_think_crate();
|
||||
if ( self.agent_type == "player" )
|
||||
self thread maps\mp\bots\_bots::bot_think_level_actions();
|
||||
else if ( self.agent_type == "odin_juggernaut" )
|
||||
self thread maps\mp\bots\_bots::bot_think_level_actions( 128 );
|
||||
self thread maps\mp\bots\_bots_strategy::bot_think_tactical_goals();
|
||||
self thread [[ self agentFunc("think") ]]();
|
||||
|
||||
if ( !self.hasDied )
|
||||
self maps\mp\gametypes\_spawnlogic::addToParticipantsArray();
|
||||
|
||||
if ( !self.hasDied )
|
||||
{
|
||||
// These functions don't need to be restarted if an agent is respawning, since they are infinite while loops
|
||||
self thread maps\mp\gametypes\_weapons::onPlayerSpawned();
|
||||
self thread maps\mp\gametypes\_battlechatter_mp::onPlayerSpawned();
|
||||
}
|
||||
|
||||
self.hasDied = false;
|
||||
|
||||
self thread maps\mp\gametypes\_healthoverlay::playerHealthRegen();
|
||||
|
||||
if ( IsDefined(self.use_randomized_personality) && self.use_randomized_personality && IsDefined(self.respawn_on_death) && self.respawn_on_death )
|
||||
{
|
||||
// Devgui agents need a costume (otherwise they'll be invisible)
|
||||
self SetAgentCostumeIndex( 1 );
|
||||
}
|
||||
|
||||
level notify( "spawned_agent_player", self );
|
||||
level notify( "spawned_agent", self );
|
||||
level notify( "player_spawned", self );
|
||||
self notify( "spawned_player" );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// destroyOnOwnerDisconnect
|
||||
//========================================================
|
||||
destroyOnOwnerDisconnect( owner )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
owner waittill( "killstreak_disowned" );
|
||||
|
||||
self notify( "owner_disconnect" );
|
||||
|
||||
// Wait till host migration finishes before suiciding
|
||||
if ( maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone() )
|
||||
wait 0.05;
|
||||
|
||||
// kill the agent
|
||||
self Suicide();
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// agent_damage_finished
|
||||
//========================================================
|
||||
agent_damage_finished( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if( IsDefined( eInflictor ) || IsDefined( eAttacker ) )
|
||||
{
|
||||
if( !IsDefined( eInflictor ) )
|
||||
eInflictor = eAttacker;
|
||||
|
||||
if( isdefined(self.allowVehicleDamage) && !self.allowVehicleDamage )
|
||||
{
|
||||
if( IsDefined( eInflictor.classname ) && eInflictor.classname == "script_vehicle" )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( IsDefined( eInflictor.classname ) && eInflictor.classname == "auto_turret" )
|
||||
eAttacker = eInflictor;
|
||||
|
||||
if( IsDefined( eAttacker ) && sMeansOfDeath != "MOD_FALLING" && sMeansOfDeath != "MOD_SUICIDE" )
|
||||
{
|
||||
if( level.teamBased )
|
||||
{
|
||||
if( IsDefined( eAttacker.team ) && eAttacker.team != self.team )
|
||||
{
|
||||
self SetAgentAttacker( eAttacker );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self SetAgentAttacker( eAttacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert(IsDefined(self.isActive) && self.isActive);
|
||||
result = self FinishAgentDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset, 0.0 );
|
||||
if ( IsDefined(result) )
|
||||
self thread FinishAgentDamage_ImpactFXWrapper( result[0], result[1], result[2], result[3], result[4], result[5], result[6] );
|
||||
|
||||
if ( !IsDefined(self.isActive) )
|
||||
{
|
||||
// Agent just died and cleared out all his script variables
|
||||
// So don't allow this agent to be freed up until he is properly deactivated in deactivateAgentDelayed
|
||||
self.waitingToDeactivate = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FinishAgentDamage_ImpactFXWrapper( attacker, mod, weapon, hitloc, point, dir, localdir )
|
||||
{
|
||||
waittillframeend; // Need to wait till the end of the frame here, so that we can spawn the impact fx at a more accurate location
|
||||
|
||||
if ( !isDefined(self) || !IsDefined(attacker) )
|
||||
return;
|
||||
|
||||
self FinishAgentDamage_ImpactFX( attacker, mod, weapon, hitloc, point, dir, localdir );
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// on_agent_generic_damaged
|
||||
//=======================================================
|
||||
on_agent_generic_damaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
attckerIsOwner = IsDefined(eAttacker) && IsDefined(self.owner) && (self.owner == eAttacker);
|
||||
attackerIsTeammate = attackerIsHittingTeam( self.owner, eAttacker ) || attckerIsOwner;
|
||||
|
||||
// ignore friendly fire damage for team based modes
|
||||
if( level.teambased && attackerIsTeammate && !level.friendlyfire )
|
||||
return false;
|
||||
|
||||
// ignore damage from owner in non team based modes
|
||||
if( !level.teambased && attckerIsOwner )
|
||||
return false;
|
||||
|
||||
// don't let helicopters and other vehicles crush a player, if we want it to then put in a special case here
|
||||
if( IsDefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_CRUSH" && IsDefined( eInflictor ) && IsDefined( eInflictor.classname ) && eInflictor.classname == "script_vehicle" )
|
||||
return false;
|
||||
|
||||
if ( !IsDefined( self ) || !isReallyAlive( self ) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( eAttacker ) && eAttacker.classname == "script_origin" && IsDefined( eAttacker.type ) && eAttacker.type == "soft_landing" )
|
||||
return false;
|
||||
|
||||
if ( sWeapon == "killstreak_emp_mp" )
|
||||
return false;
|
||||
|
||||
if ( sWeapon == "bouncingbetty_mp" && !maps\mp\gametypes\_weapons::mineDamageHeightPassed( eInflictor, self ) )
|
||||
return false;
|
||||
|
||||
// ensure throwing knife death
|
||||
if ( ( sWeapon == "throwingknife_mp" || sWeapon == "throwingknifejugg_mp" ) && sMeansOfDeath == "MOD_IMPACT" )
|
||||
iDamage = self.health + 1;
|
||||
|
||||
// ensures stuck death
|
||||
if ( IsDefined( eInflictor ) && IsDefined( eInflictor.stuckEnemyEntity ) && eInflictor.stuckEnemyEntity == self )
|
||||
iDamage = self.health + 1;
|
||||
|
||||
if( iDamage <= 0 )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( eAttacker ) && eAttacker != self && iDamage > 0 && ( !IsDefined( sHitLoc ) || sHitLoc != "shield" ) )
|
||||
{
|
||||
if( iDFlags & level.iDFLAGS_STUN )
|
||||
typeHit = "stun";
|
||||
else if( !shouldWeaponFeedback( sWeapon ) )
|
||||
typeHit = "none";
|
||||
else
|
||||
typeHit = "standard";
|
||||
|
||||
eAttacker thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback( typeHit );
|
||||
}
|
||||
|
||||
if ( IsDefined( level.modifyPlayerDamage ) )
|
||||
iDamage = [[level.modifyPlayerDamage]]( self, eAttacker, iDamage, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc );
|
||||
|
||||
return self [[ self agentFunc( "on_damaged_finished" ) ]]( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// on_agent_player_damaged
|
||||
//========================================================
|
||||
on_agent_player_damaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
attckerIsOwner = IsDefined(eAttacker) && IsDefined(self.owner) && (self.owner == eAttacker);
|
||||
|
||||
// ignore damage from owner in non team based modes
|
||||
if( !level.teambased && attckerIsOwner )
|
||||
return false;
|
||||
|
||||
Callback_PlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_player_killed
|
||||
//=======================================================
|
||||
on_agent_player_killed(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
self on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, true);
|
||||
|
||||
// award XP for killing agents
|
||||
if( isPlayer( eAttacker ) && (!isDefined(self.owner) || eAttacker != self.owner) && (!IsDefined(self.nonKillstreakAgent) || !self.nonKillstreakAgent) )
|
||||
{
|
||||
// TODO: should play vo for killing the agent
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_squad_mate" );
|
||||
}
|
||||
|
||||
if( IsDefined(level.on_agent_player_killed) )
|
||||
[[level.on_agent_player_killed]](eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration);
|
||||
|
||||
self thread maps\mp\gametypes\_weapons::dropScavengerForDeath( eAttacker );
|
||||
|
||||
if ( self.isActive )
|
||||
{
|
||||
self.hasDied = true;
|
||||
|
||||
if ( getGametypeNumLives() != 1 && ( IsDefined(self.respawn_on_death) && self.respawn_on_death ) )
|
||||
{
|
||||
self thread [[ self agentFunc("spawn") ]]();
|
||||
}
|
||||
else
|
||||
{
|
||||
self deactivateAgent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_humanoid_agent_killed_common
|
||||
//=======================================================
|
||||
on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, dropWeapons )
|
||||
{
|
||||
// Things that happen on every type of humanoid agent that dies
|
||||
|
||||
if ( self maps\mp\_riotshield::hasRiotShieldEquipped() )
|
||||
{
|
||||
self LaunchShield( iDamage, sMeansofDeath );
|
||||
|
||||
if ( !dropWeapons )
|
||||
{
|
||||
// If not dropping weapons, need to make sure we at least drop the riot shield
|
||||
item = self dropItem( self GetCurrentWeapon() );
|
||||
|
||||
if( IsDefined(item) )
|
||||
{
|
||||
item thread maps\mp\gametypes\_weapons::deletePickupAfterAWhile();
|
||||
item.owner = self;
|
||||
item.ownersattacker = eAttacker;
|
||||
item MakeUnusable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( dropWeapons )
|
||||
self thread [[level.weaponDropFunction]]( eAttacker, sMeansOfDeath );
|
||||
|
||||
// ragdoll
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
thread delayStartRagdoll( self.body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath );
|
||||
|
||||
self maps\mp\_riotshield::riotShield_clear();
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// initPlayerClass
|
||||
//===========================================
|
||||
initPlayerClass()
|
||||
{
|
||||
// Must be called AFTER agent has been spawned as a bot agent
|
||||
if ( IsDefined(self.class_override) )
|
||||
{
|
||||
self.class = self.class_override;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self maps\mp\bots\_bots_loadout::bot_setup_loadout_callback() )
|
||||
self.class = "callback";
|
||||
else
|
||||
self.class = "class1";
|
||||
}
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_ball.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_ball.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
69
raw/maps/mp/agents/_agents_gametype_conf.gsc
Normal file
69
raw/maps/mp/agents/_agents_gametype_conf.gsc
Normal file
@ -0,0 +1,69 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"] = ::agent_squadmember_conf_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_conf_think;
|
||||
}
|
||||
|
||||
agent_player_conf_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_conf::bot_conf_think();
|
||||
}
|
||||
|
||||
agent_squadmember_conf_think()
|
||||
{
|
||||
// Returning true means the "think" was handled here. "False" means use the default think
|
||||
|
||||
if ( !IsDefined(self.tags_seen_by_owner) )
|
||||
self.tags_seen_by_owner = [];
|
||||
|
||||
if ( !IsDefined(self.next_time_check_tags) )
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
if ( GetTime() > self.next_time_check_tags )
|
||||
{
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
current_player_fov = 0.78; // approximation
|
||||
nearest_node_to_player = self.owner GetNearestNode();
|
||||
if ( IsDefined(nearest_node_to_player) )
|
||||
{
|
||||
new_visible_tags_to_player = self.owner maps\mp\bots\_bots_gametype_conf::bot_find_visible_tags( true, nearest_node_to_player, current_player_fov );
|
||||
self.tags_seen_by_owner = maps\mp\bots\_bots_gametype_conf::bot_combine_tag_seen_arrays( new_visible_tags_to_player, self.tags_seen_by_owner );
|
||||
}
|
||||
}
|
||||
|
||||
self.tags_seen_by_owner = self maps\mp\bots\_bots_gametype_conf::bot_remove_invalid_tags( self.tags_seen_by_owner );
|
||||
best_tag = self maps\mp\bots\_bots_gametype_conf::bot_find_best_tag_from_array( self.tags_seen_by_owner, false );
|
||||
|
||||
if ( IsDefined(best_tag) )
|
||||
{
|
||||
if ( !IsDefined(self.tag_getting) || DistanceSquared(best_tag.curorigin, self.tag_getting.curorigin) > 1 )
|
||||
{
|
||||
self.tag_getting = best_tag;
|
||||
self bot_defend_stop();
|
||||
self BotSetScriptGoal( self.tag_getting.curorigin, 0, "objective", undefined, level.bot_tag_obj_radius );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( IsDefined(self.tag_getting) )
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
self.tag_getting = undefined;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
17
raw/maps/mp/agents/_agents_gametype_ctf.gsc
Normal file
17
raw/maps/mp/agents/_agents_gametype_ctf.gsc
Normal file
@ -0,0 +1,17 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = maps\mp\bots\_bots_gametype_ctf::bot_ctf_think;
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_dm.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_dm.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
49
raw/maps/mp/agents/_agents_gametype_dom.gsc
Normal file
49
raw/maps/mp/agents/_agents_gametype_dom.gsc
Normal file
@ -0,0 +1,49 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"]= ::agent_squadmember_dom_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_dom_think;
|
||||
}
|
||||
|
||||
agent_player_dom_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_dom::bot_dom_think();
|
||||
}
|
||||
|
||||
agent_squadmember_dom_think()
|
||||
{
|
||||
// Returning true means the "think" was handled here. "False" means use the default think
|
||||
|
||||
owner_flag = undefined;
|
||||
foreach( trigger in self.owner.touchTriggers )
|
||||
{
|
||||
if ( trigger.useobj.id == "domFlag" )
|
||||
owner_flag = trigger;
|
||||
}
|
||||
|
||||
if ( IsDefined(owner_flag) )
|
||||
{
|
||||
owner_flag_team = owner_flag maps\mp\gametypes\dom::getFlagTeam();
|
||||
if ( owner_flag_team != self.team )
|
||||
{
|
||||
if ( !self maps\mp\bots\_bots_gametype_dom::bot_is_capturing_flag( owner_flag ) )
|
||||
self maps\mp\bots\_bots_gametype_dom::capture_flag(owner_flag, "critical", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_gun.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_gun.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
702
raw/maps/mp/agents/_agents_gametype_horde.gsc
Normal file
702
raw/maps/mp/agents/_agents_gametype_horde.gsc
Normal file
@ -0,0 +1,702 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_horde_util;
|
||||
#include maps\mp\gametypes\_horde_crates;
|
||||
#include maps\mp\gametypes\_horde_drones;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
/#
|
||||
CONST_DISABLE_SPAWNING = false;
|
||||
CONST_FORCE_DOG_SPAWN = false;
|
||||
CONST_FORCE_DRONE_SPAWN = false;
|
||||
CONST_FORCE_PLAYER_ENEMY_SPAWN = false;
|
||||
CONST_DISABLE_AUTO_AI_REMOVAL = false;
|
||||
#/
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
level thread runRoundSpawning();
|
||||
|
||||
//for spot underneath the trailer where players can exploit goliaths
|
||||
if ( getMapName() == "mp_detroit" )
|
||||
level.goliathExploitTrigger = Spawn( "trigger_radius", ( -1662, -72, 582.5 ), 0, 86, 64 );
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["player"]["think"] = ::enemyAgentThink;
|
||||
level.agent_funcs["player"]["on_killed"] = ::onAgentKilled;
|
||||
|
||||
level.agent_funcs["squadmate"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["squadmate"]["think"] = ::allyAgentThink;
|
||||
|
||||
level.agent_funcs["dog"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["dog"]["think"] = ::agentDogThink;
|
||||
level.agent_funcs["dog"]["on_killed"] = ::onDogKilled;
|
||||
}
|
||||
|
||||
onAIConnect()
|
||||
{
|
||||
self.gameModefirstSpawn = true;
|
||||
self.agentname = &"HORDE_GRUNT";
|
||||
self.horde_type = "";
|
||||
}
|
||||
|
||||
runRoundSpawning()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "start_round" );
|
||||
|
||||
/#
|
||||
if ( CONST_DISABLE_SPAWNING )
|
||||
continue;
|
||||
#/
|
||||
|
||||
wait 2; //give a little extra time for enemy weapons to load
|
||||
|
||||
if ( getMapName() == "mp_prison_z" && level.currentRoundNumber > 10 )
|
||||
runZombieRound();
|
||||
else
|
||||
runNormalRound();
|
||||
}
|
||||
}
|
||||
|
||||
runNormalRound()
|
||||
{
|
||||
level childthread highlightLastEnemies();
|
||||
|
||||
while( level.currentEnemyCount < level.maxEnemyCount )
|
||||
{
|
||||
while( level.currentAliveEnemyCount < level.maxAliveEnemyCount )
|
||||
{
|
||||
createEnemy();
|
||||
|
||||
if( level.currentEnemyCount == level.maxEnemyCount )
|
||||
break;
|
||||
}
|
||||
|
||||
level.waveFirstSpawn = false;
|
||||
|
||||
level waittill( "enemy_death" );
|
||||
}
|
||||
}
|
||||
|
||||
runZombieRound()
|
||||
{
|
||||
|
||||
level.zombiesDead = 0;
|
||||
level waittill ( "beginZombieSpawn" ); //delay start of spawning for VO - event, etc.
|
||||
|
||||
while( level.currentEnemyCount < level.maxEnemyCount )
|
||||
{
|
||||
while( level.currentAliveEnemyCount < level.maxAliveEnemyCount )
|
||||
{
|
||||
createEnemy();
|
||||
wait 0.1;
|
||||
}
|
||||
|
||||
level.waveFirstSpawn = false;
|
||||
|
||||
level waittill_any ( "enemy_death", "go_zombie" );
|
||||
|
||||
level.zombiesDead++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createEnemy()
|
||||
{
|
||||
/#
|
||||
if( CONST_FORCE_DOG_SPAWN )
|
||||
{
|
||||
createDogEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
/#
|
||||
if( CONST_FORCE_PLAYER_ENEMY_SPAWN )
|
||||
{
|
||||
createHumanoidEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
/#
|
||||
if( CONST_FORCE_DRONE_SPAWN )
|
||||
{
|
||||
createDroneEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
if( level.maxDogCount > 1 && level.dogsAlive < level.maxDogCount ) //no dogs at the same time as drones for now
|
||||
{
|
||||
createDogEnemy();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( level.maxWarbirdCount > 0 )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( isOnHumanTeam( player ) && IsAlive( player ) )
|
||||
{
|
||||
player createWarbirdEnemy(); //use player's origin as a starting point.
|
||||
level.maxWarbirdCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( level.maxDroneCount > 0 )
|
||||
{
|
||||
createDroneEnemy();
|
||||
level.maxDroneCount --;
|
||||
}
|
||||
else
|
||||
createHumanoidEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
createHumanoidEnemy()
|
||||
{
|
||||
agent = undefined;
|
||||
|
||||
while( !IsDefined(agent) )
|
||||
{
|
||||
agent = maps\mp\agents\_agents::add_humanoid_agent( "player", level.enemyTeam, "class1" );
|
||||
|
||||
if( IsDefined(agent) )
|
||||
{
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
createDogEnemy()
|
||||
{
|
||||
agent = undefined;
|
||||
|
||||
while( !IsDefined(agent) )
|
||||
{
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( "dog", level.enemyTeam );
|
||||
if( IsDefined(agent) )
|
||||
{
|
||||
agent thread [[ agent agentFunc("spawn") ]]();
|
||||
agent.awardpoints = 100;
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
level.dogsAlive++;
|
||||
}
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
createDroneEnemy()
|
||||
{
|
||||
// Wait until an empty vehicle slot is available to spawn this drone
|
||||
thread waitingToSpawnDrone();
|
||||
|
||||
// Claim the enemy spot for our future drone
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
}
|
||||
|
||||
waitingToSpawnDrone()
|
||||
{
|
||||
level.numDronesWaitingToSpawn++;
|
||||
|
||||
// Leave 2 open vehicle slots for players to use
|
||||
while( currentActiveVehicleCount( 2 ) >= maxVehiclesAllowed() )
|
||||
{
|
||||
wait 1;
|
||||
}
|
||||
|
||||
level.numDronesWaitingToSpawn--;
|
||||
|
||||
waitframe();
|
||||
drone = hordeCreateDrone ( level.players[0], "assault_uav_horde", level.hordeDroneModel );
|
||||
|
||||
drone HudOutlineEnable( level.enemyOutlineColor, true );
|
||||
drone.droneturret HudOutlineEnable ( level.enemyOutlineColor, true );
|
||||
drone.outlineColor = level.enemyOutlineColor;
|
||||
}
|
||||
|
||||
createWarbirdEnemy()
|
||||
{
|
||||
self thread maps\mp\gametypes\_horde_warbird::hordeCreateWarbird();
|
||||
}
|
||||
|
||||
playAISpawnEffect()
|
||||
{
|
||||
PlayFX( level._effect["spawn_effect"], self.origin );
|
||||
}
|
||||
|
||||
highlightLastEnemies()
|
||||
{
|
||||
level endon( "round_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "enemy_death" );
|
||||
|
||||
if( level.currentEnemyCount != level.maxEnemyCount )
|
||||
continue;
|
||||
|
||||
if( level.currentAliveEnemyCount < 3 )
|
||||
{
|
||||
foreach ( drone in level.flying_attack_drones )
|
||||
{
|
||||
drone HudOutlineEnable ( level.enemyOutlineColor, false );
|
||||
drone.droneTurret HudOutlineEnable ( level.enemyOutlineColor, false );
|
||||
drone.lastTwoEnemies = true;
|
||||
}
|
||||
foreach( player in level.characters )
|
||||
{
|
||||
if( isOnHumanTeam(player) )
|
||||
continue;
|
||||
|
||||
if( isReallyAlive(player) && !player IsCloaked() )
|
||||
{
|
||||
player HudOutlineEnable( level.enemyOutlineColor, false );
|
||||
player.outlineColor = level.enemyOutlineColor;
|
||||
}
|
||||
}
|
||||
setdvar ( "bg_compassShowEnemies", 1 );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAgentKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
if( !isOnHumanTeam(self) )
|
||||
self hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
self HudOutlineDisable();
|
||||
self maps\mp\agents\_agents::on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, false);
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
}
|
||||
|
||||
onDogKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
if( !isOnHumanTeam(self) )
|
||||
self hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
self HudOutlineDisable();
|
||||
self maps\mp\killstreaks\_dog_killstreak::on_agent_dog_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
//level.dogsAlive--;
|
||||
}
|
||||
|
||||
hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
AssertEx( (level.currentAliveEnemyCount > 0), "currentAliveEnemyCount is below zero" );
|
||||
|
||||
level.currentAliveEnemyCount--;
|
||||
level.killsSinceIntelDrop ++;
|
||||
level.killsSinceAmmoDrop ++;
|
||||
|
||||
if ( level.objDefend )
|
||||
maps\mp\gametypes\horde::checkDefendKill( self, eAttacker );
|
||||
|
||||
trackIntelKills( sWeapon, sMeansOfDeath );
|
||||
|
||||
level thread maps\mp\gametypes\horde::chanceToSpawnPickup( self );
|
||||
level notify( "enemy_death", eAttacker, self );
|
||||
|
||||
//for enemies left UI in HordeHud.lua
|
||||
level.enemiesLeft--;
|
||||
if ( !level.zombiesStarted ) //don't update the UI after zombies has started
|
||||
SetOmnvar ( "ui_horde_enemies_left", level.enemiesLeft );
|
||||
|
||||
// player attacker
|
||||
if ( IsPlayer( eAttacker ) && !level.zombiesStarted )
|
||||
{
|
||||
awardHordeKill( eAttacker );
|
||||
eAttacker thread maps\mp\gametypes\_rank::xpPointsPopup( "kill", self.awardPoints );
|
||||
level thread hordeUpdateScore( eAttacker, self.awardPoints );
|
||||
|
||||
if( eAttacker _hasPerk("specialty_triggerhappy") )
|
||||
{
|
||||
// lost with port to blacksmith
|
||||
//eAttacker thread maps\mp\perks\_perkfunctions::setTriggerHappyInternal();
|
||||
}
|
||||
}
|
||||
|
||||
// killstreak entity attacker
|
||||
if( IsDefined(eAttacker) && IsDefined(eAttacker.owner) && IsPlayer(eAttacker.owner) && IsDefined(eAttacker.owner.killz) )
|
||||
{
|
||||
awardHordeKill( eAttacker.owner );
|
||||
eAttacker.owner thread maps\mp\gametypes\_rank::xpPointsPopup( "kill", self.awardPoints );
|
||||
level thread hordeUpdateScore( eAttacker.owner, self.awardPoints );
|
||||
}
|
||||
}
|
||||
|
||||
trackIntelKills( sWeapon, sMeansOfDeath )
|
||||
{
|
||||
if( level.isTeamIntelComplete )
|
||||
return;
|
||||
|
||||
if( sWeapon == "none" )
|
||||
return;
|
||||
|
||||
if( isMeleeMOD(sMeansOfDeath) )
|
||||
level.numMeleeKillsIntel++;
|
||||
|
||||
if( !isKillstreakWeapon( sWeapon ) && (sMeansOfDeath == "MOD_HEAD_SHOT") )
|
||||
level.numHeadShotsIntel++;
|
||||
|
||||
if( isKillstreakWeapon( sWeapon ) && (sWeapon != level.intelMiniGun) )
|
||||
level.numKillStreakKillsIntel++;
|
||||
|
||||
if( maps\mp\gametypes\_class::isValidEquipment( sWeapon, false ) || maps\mp\gametypes\_class::isValidOffhand( sWeapon, false ) )
|
||||
level.numEquipmentKillsIntel++;
|
||||
}
|
||||
|
||||
enemyAgentThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self BotSetFlag("no_enemy_search", true);
|
||||
|
||||
self thread monitorBadHumanoidAI();
|
||||
self thread locateEnemyPositions();
|
||||
}
|
||||
|
||||
monitorBadHumanoidAI()
|
||||
{
|
||||
/#
|
||||
if( CONST_DISABLE_AUTO_AI_REMOVAL )
|
||||
return;
|
||||
#/
|
||||
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
spawnTime = GetTime();
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait( 5.0 );
|
||||
|
||||
if( !bot_in_combat(120 * 1000) )
|
||||
{
|
||||
|
||||
if( !bot_in_combat(240 * 1000) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 240, 480 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
//removing this for the time being. Will see if problems arise
|
||||
//killAgent( self );
|
||||
}
|
||||
|
||||
monitorBadDogAI()
|
||||
{
|
||||
/#
|
||||
if( CONST_DISABLE_AUTO_AI_REMOVAL )
|
||||
return;
|
||||
#/
|
||||
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
spawnTime = GetTime();
|
||||
lastPosition = self.origin;
|
||||
lastPositionTime = spawnTime;
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait( 5.0 );
|
||||
|
||||
positionDelta = DistanceSquared( self.origin, lastPosition );
|
||||
positionTime = (GetTime() - lastPositionTime) / 1000;
|
||||
|
||||
if( positionDelta > (128 * 128) )
|
||||
{
|
||||
lastPosition = self.origin;
|
||||
lastPositionTime = GetTime();
|
||||
}
|
||||
else if( positionTime > 25 )
|
||||
{
|
||||
if( positionTime > 55 )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 120, 240 ) )
|
||||
break;
|
||||
}
|
||||
killAgent( self );
|
||||
}
|
||||
|
||||
checkExpireTime( spawnTime, highLightTime, expireTime )
|
||||
{
|
||||
aliveTime = (GetTime() - spawnTime) / 1000;
|
||||
|
||||
if( aliveTime > highLightTime )
|
||||
{
|
||||
|
||||
if( aliveTime > expireTime )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SCR_CONST_ALLY_AGENT_LOW_HEALTH_BEHAVIOR = 0.6;
|
||||
SCR_CONST_PLAYER_LOW_HEALTH_BEHAVIOR = 0.5;
|
||||
|
||||
allyAgentThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
self BotSetFlag("force_sprint",true);
|
||||
holding_till_health_regen = false;
|
||||
next_time_protect_player = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if ( float(self.owner.health) / self.owner.maxhealth < SCR_CONST_PLAYER_LOW_HEALTH_BEHAVIOR && GetTime() > next_time_protect_player )
|
||||
{
|
||||
nodes = GetNodesInRadiusSorted(self.owner.origin, 256, 0);
|
||||
if ( nodes.size >= 2 )
|
||||
{
|
||||
self.defense_force_next_node_goal = nodes[1]; // Send agent to the second-closest node to the player
|
||||
self notify("defend_force_node_recalculation");
|
||||
next_time_protect_player = GetTime() + 1000;
|
||||
}
|
||||
}
|
||||
else if ( float(self.health) / self.maxhealth >= SCR_CONST_ALLY_AGENT_LOW_HEALTH_BEHAVIOR )
|
||||
{
|
||||
holding_till_health_regen = false;
|
||||
}
|
||||
else if ( !holding_till_health_regen )
|
||||
{
|
||||
// Pick node on the opposite side of the player and hide at it
|
||||
node = self bot_find_node_to_guard_player( self.owner.origin, 350, true );
|
||||
if ( IsDefined(node) )
|
||||
{
|
||||
self.defense_force_next_node_goal = node;
|
||||
self notify("defend_force_node_recalculation");
|
||||
|
||||
holding_till_health_regen = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !self bot_is_guarding_player( self.owner ) )
|
||||
{
|
||||
optional_params["override_goal_type"] = "critical";
|
||||
optional_params["min_goal_time"] = 20;
|
||||
optional_params["max_goal_time"] = 30;
|
||||
self bot_guard_player( self.owner, 350, optional_params );
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
hordeSetupDogState()
|
||||
{
|
||||
//self _setNameplateMaterial( "player_name_bg_green_dog", "player_name_bg_red_dog" );
|
||||
self.enableExtendedKill = false;
|
||||
self.agentname = &"HORDE_QUAD";
|
||||
self.horde_type = "Quad";
|
||||
self thread maps\mp\gametypes\horde::hordeApplyAIModifiers();
|
||||
// pathing variables
|
||||
self.lasSetGoalPos = (0,0,0);
|
||||
self.bHasNoPath = false;
|
||||
self.randomPathStopTime = 0;
|
||||
|
||||
self.maxhealth = 60;
|
||||
self.health = self.maxhealth;
|
||||
}
|
||||
|
||||
agentDogThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
self maps\mp\agents\dog\_dog_think::setupDogState();
|
||||
self hordeSetupDogState();
|
||||
|
||||
self thread locateEnemyPositions();
|
||||
self thread [[self.watchAttackStateFunc]]();
|
||||
self thread WaitForBadPathHorde();
|
||||
self thread monitorBadDogAI();
|
||||
self thread agentDogBark();
|
||||
|
||||
/#
|
||||
self thread maps\mp\agents\dog\_dog_think::debug_dog();
|
||||
#/
|
||||
|
||||
while ( true )
|
||||
{
|
||||
/#
|
||||
if ( self maps\mp\agents\dog\_dog_think::ProcessDebugMode() )
|
||||
continue;
|
||||
#/
|
||||
|
||||
if ( self.aiState != "melee" && !self.stateLocked && self maps\mp\agents\dog\_dog_think::readyToMeleeTarget() && !self maps\mp\agents\dog\_dog_think::DidPastMeleeFail() )
|
||||
self ScrAgentBeginMelee( self.curMeleeTarget );
|
||||
|
||||
if( self.randomPathStopTime > GetTime() )
|
||||
{
|
||||
wait(0.05);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !IsDefined(self.enemy) || self.bHasNoPath )
|
||||
{
|
||||
pathNodes = GetNodesInRadiusSorted( self.origin, 1024, 256, 128, "Path" );
|
||||
|
||||
if( pathNodes.size > 0 )
|
||||
{
|
||||
nodeNum = RandomIntRange(int(pathNodes.size*0.9), pathNodes.size); //Pick from the furthest 10%
|
||||
self ScrAgentSetGoalPos( pathNodes[nodeNum].origin );
|
||||
self.bHasNoPath = false;
|
||||
self.randomPathStopTime = GetTime() + 2500;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attackPoint = self maps\mp\agents\dog\_dog_think::GetAttackPoint( self.enemy );
|
||||
self.curMeleeTarget = self.enemy;
|
||||
self.moveMode = "sprint";
|
||||
self.bArrivalsEnabled = false;
|
||||
|
||||
if( DistanceSquared(attackPoint, self.lasSetGoalPos) > (64 * 64) )
|
||||
{
|
||||
self ScrAgentSetGoalPos( attackPoint );
|
||||
self.lasSetGoalPos = attackPoint;
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
agentDogBark()
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
while ( !isdefined ( self.curmeleetarget ) )
|
||||
wait 0.25;
|
||||
|
||||
while ( isdefined ( self.curMeleeTarget ) && distance ( self.origin, self.curMeleeTarget.origin ) > 200 )
|
||||
{
|
||||
wait randomfloatrange ( 0, 2 );
|
||||
self playsound ( "anml_doberman_bark" );
|
||||
|
||||
}
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WaitForBadPathHorde()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "bad_path", badGoalPos );
|
||||
self.bHasNoPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
locateEnemyPositions()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if( isOnHumanTeam(player) )
|
||||
{
|
||||
self GetEnemyInfo( player );
|
||||
if ( isdefined ( player.hordeDrone ) )
|
||||
self GetEnemyInfo ( player.hordeDrone );
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
findClosestPlayer()
|
||||
{
|
||||
closestPlayer = undefined;
|
||||
closestDistance = 100000 * 100000;
|
||||
|
||||
// find the nearest player
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( isReallyAlive(player) && isOnHumanTeam(player) && !isPlayerInLastStand(player) )
|
||||
{
|
||||
distSquared = DistanceSquared( player.origin, self.origin );
|
||||
|
||||
if ( distSquared < closestDistance )
|
||||
{
|
||||
closestPlayer = player;
|
||||
closestDistance = distSquared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestPlayer;
|
||||
}
|
||||
|
||||
handleDetroitGoliathTrailerExploit()
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
wait 1;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( isdefined ( self.enemy ) && self.enemy IsTouching ( level.goliathExploitTrigger ) )
|
||||
{
|
||||
//IPrintLnBold ( "In Exploit Trigger" );
|
||||
self BotSetScriptGoal ( ( -1696, -408, 608.5 ), 32, "critical", 200 );
|
||||
|
||||
while ( isdefined ( self.enemy ) && isReallyAlive ( self.enemy ) && !isPlayerInLastStand ( self.enemy ) && self.enemy istouching ( level.goliathExploitTrigger ) )
|
||||
wait 0.25;
|
||||
|
||||
self BotClearScriptGoal();
|
||||
}
|
||||
|
||||
wait 1;
|
||||
}
|
||||
}
|
17
raw/maps/mp/agents/_agents_gametype_hp.gsc
Normal file
17
raw/maps/mp/agents/_agents_gametype_hp.gsc
Normal file
@ -0,0 +1,17 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = maps\mp\bots\_bots_gametype_hp::bot_hp_think;
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_infect.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_infect.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
26
raw/maps/mp/agents/_agents_gametype_sd.gsc
Normal file
26
raw/maps/mp/agents/_agents_gametype_sd.gsc
Normal file
@ -0,0 +1,26 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_sd_think;
|
||||
}
|
||||
|
||||
agent_player_sd_think()
|
||||
{
|
||||
self _enableUsability();
|
||||
foreach( bombzone in level.bombZones )
|
||||
bombzone.trigger EnablePlayerUse( self );
|
||||
|
||||
self thread maps\mp\bots\_bots_gametype_sd::bot_sd_think();
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_sr.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_sr.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
maps\mp\agents\_agents_gametype_sd::setup_callbacks();
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_twar.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_twar.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
4
raw/maps/mp/agents/_agents_gametype_vlobby.gsc
Normal file
4
raw/maps/mp/agents/_agents_gametype_vlobby.gsc
Normal file
@ -0,0 +1,4 @@
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_war.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_war.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
247
raw/maps/mp/agents/_scriptedagents.gsc
Normal file
247
raw/maps/mp/agents/_scriptedagents.gsc
Normal file
@ -0,0 +1,247 @@
|
||||
//
|
||||
// Scripted agent common functions.
|
||||
//
|
||||
|
||||
// called from code when animation state changes.
|
||||
OnEnterState( prevState, nextState )
|
||||
{
|
||||
if ( IsDefined( self.OnEnterAnimState ) )
|
||||
self [[ self.OnEnterAnimState ]]( prevState, nextState );
|
||||
}
|
||||
|
||||
// called from code when the agent is freed.
|
||||
OnDeactivate()
|
||||
{
|
||||
self notify( "killanimscript" );
|
||||
}
|
||||
|
||||
|
||||
// util function
|
||||
PlayAnimUntilNotetrack( animState, animLabel, notetrack, customFunction )
|
||||
{
|
||||
PlayAnimNUntilNotetrack( animState, 0, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex );
|
||||
|
||||
if ( !IsDefined( notetrack ) )
|
||||
notetrack = "end";
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction );
|
||||
}
|
||||
|
||||
PlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
|
||||
if ( !IsDefined( notetrack ) )
|
||||
notetrack = "end";
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction );
|
||||
}
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction )
|
||||
{
|
||||
startTime = getTime();
|
||||
animTime = undefined;
|
||||
animLength = undefined;
|
||||
|
||||
if ( isDefined ( animState ) && isDefined ( animIndex ) )
|
||||
animLength = getAnimLength( self GetAnimEntry( animState, animIndex ));
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( animLabel, note );
|
||||
|
||||
if ( isDefined ( animLength ) )
|
||||
animTime = ( getTime() - startTime ) * 0.001 / animLength;
|
||||
|
||||
if ( !isDefined( animLength ) || animTime > 0 )
|
||||
{
|
||||
if ( note == notetrack || note == "end" || note == "anim_will_finish" || note == "finish" )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( customFunction ) )
|
||||
[[ customFunction ]]( note, animState, animIndex, animTime );
|
||||
}
|
||||
}
|
||||
|
||||
PlayAnimForTime( animState, time )
|
||||
{
|
||||
PlayAnimNForTime( animState, 0, time );
|
||||
}
|
||||
|
||||
PlayAnimNForTime( animState, animIndex, time )
|
||||
{
|
||||
self SetAnimState( animState, animIndex );
|
||||
wait( time );
|
||||
}
|
||||
|
||||
PlayAnimNAtRateForTime( animState, animIndex, animRate, time )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
wait( time );
|
||||
}
|
||||
|
||||
GetAnimScaleFactors( delta, animDelta, bAnimInWorldSpace )
|
||||
{
|
||||
distXY = Length2D( delta );
|
||||
distZ = delta[2];
|
||||
animXY = Length2D( animDelta );
|
||||
animZ = animDelta[2];
|
||||
|
||||
scaleXY = 1;
|
||||
scaleZ = 1;
|
||||
if ( IsDefined( bAnimInWorldSpace ) && bAnimInWorldSpace )
|
||||
{
|
||||
animDelta2D = ( animDelta[0], animDelta[1], 0 );
|
||||
animDeltaDir = VectorNormalize( animDelta2D );
|
||||
if ( VectorDot( animDeltaDir, delta ) < 0 )
|
||||
scaleXY = 0;
|
||||
else if ( animXY > 0 )
|
||||
scaleXY = distXY / animXY;
|
||||
}
|
||||
else if ( animXY > 0 )
|
||||
scaleXY = distXY / animXY;
|
||||
|
||||
assert( scaleXY >= 0 );
|
||||
|
||||
if ( abs(animZ) > 0.001 && animZ * distZ >= 0 ) // animZ & distZ have to be same sign.
|
||||
scaleZ = distZ / animZ;
|
||||
|
||||
assert( scaleZ >= 0 );
|
||||
|
||||
scaleFactors = SpawnStruct();
|
||||
scaleFactors.xy = scaleXY;
|
||||
scaleFactors.z = scaleZ;
|
||||
|
||||
return scaleFactors;
|
||||
}
|
||||
|
||||
// -180, -135, -90, -45, 0, 45, 90, 135, 180
|
||||
// favor underturning, unless you're within <threshold> degrees of the next one up.
|
||||
GetAngleIndex( angle, threshold )
|
||||
{
|
||||
if ( !IsDefined( threshold ) )
|
||||
threshold = 10;
|
||||
|
||||
if ( angle < 0 )
|
||||
return int( ceil( ( 180 + angle - threshold ) / 45 ) );
|
||||
else
|
||||
return int( floor( ( 180 + angle + threshold ) / 45 ) );
|
||||
}
|
||||
|
||||
|
||||
DropPosToGround( position, drop_distance )
|
||||
{
|
||||
//droppedPos = GetGroundPosition( position, radius, 64, 64 );
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
if ( !IsDefined( drop_distance ) )
|
||||
drop_distance = 18;
|
||||
|
||||
startPos = position + (0, 0, drop_distance);
|
||||
endPos = position + (0, 0, drop_distance * -1 );
|
||||
|
||||
droppedPos = self AIPhysicsTrace( startPos, endPos, self.radius, self.height, true );
|
||||
|
||||
if ( abs( droppedPos[2] - startPos[2] ) < 0.1 )
|
||||
return undefined;
|
||||
|
||||
if ( abs( droppedPos[2] - endPos[2] ) < 0.1 )
|
||||
return undefined;
|
||||
|
||||
return droppedPos;
|
||||
}
|
||||
|
||||
|
||||
TRACE_RADIUS_BUFFER = 4;
|
||||
CanMovePointToPoint( startPos, endPos, stepSize )
|
||||
{
|
||||
if ( !isDefined( stepSize ) )
|
||||
{
|
||||
stepSize = 6;
|
||||
}
|
||||
|
||||
step_offset = (0, 0, 1) * stepSize;
|
||||
startPosRaised = startPos + step_offset;
|
||||
endPosRaised = endPos + step_offset;
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
assert( stepSize < self.height );
|
||||
|
||||
return self AIPhysicsTracePassed( startPosRaised, endPosRaised, self.radius, self.height - stepSize, true );
|
||||
}
|
||||
|
||||
GetValidPointToPointMoveLocation( startPos, endPos, stepSize )
|
||||
{
|
||||
if ( !isDefined( stepSize ) )
|
||||
{
|
||||
stepSize = 6;
|
||||
}
|
||||
|
||||
step_offset = (0, 0, 1) * stepSize;
|
||||
startPosRaised = startPos + step_offset;
|
||||
endPosRaised = endPos + step_offset;
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
assert( stepSize < self.height );
|
||||
|
||||
return self AIPhysicsTrace( startPosRaised, endPosRaised, self.radius + TRACE_RADIUS_BUFFER, self.height - stepSize, true );
|
||||
}
|
||||
|
||||
GetSafeAnimMoveDeltaPercentage( moveAnim )
|
||||
{
|
||||
animTranslation = GetMoveDelta( moveAnim );
|
||||
endPos = self LocalToWorldCoords( animTranslation );
|
||||
validMovePosition = GetValidPointToPointMoveLocation( self.origin, endPos );
|
||||
validMoveDistance = Distance( self.origin, validMovePosition );
|
||||
desiredMoveDistance = Distance( self.origin, endPos );
|
||||
|
||||
return Min( 1.0, validMoveDistance / desiredMoveDistance );
|
||||
}
|
||||
|
||||
SafelyPlayAnimUntilNotetrack( animState, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimAtRateUntilNotetrack( animState, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
SafelyPlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animToPlay = self GetAnimEntry( animState, animIndex );
|
||||
moveScale = GetSafeAnimMoveDeltaPercentage( animToPlay );
|
||||
self ScrAgentSetAnimScale( moveScale, 1.0 );
|
||||
self PlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
}
|
||||
|
||||
GetRandomAnimEntry( state )
|
||||
{
|
||||
count = self GetAnimEntryCount( state );
|
||||
return RandomInt( count );
|
||||
}
|
||||
|
||||
GetAngleIndexFromSelfYaw( targetVector )
|
||||
{
|
||||
targetAngles = VectorToAngles( targetVector );
|
||||
angleDiff = AngleClamp180( targetAngles[1] - self.angles[1] );
|
||||
return GetAngleIndex( angleDiff );
|
||||
}
|
303
raw/maps/mp/agents/dog/_dog_idle.gsc
Normal file
303
raw/maps/mp/agents/dog/_dog_idle.gsc
Normal file
@ -0,0 +1,303 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self.animSubstate = "none";
|
||||
|
||||
self SetTimeOfNextSound();
|
||||
self.timeOfNextSound += 2000;
|
||||
|
||||
self.bIdleHitReaction = false;
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
|
||||
self UpdateState();
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
if ( IsDefined( self.prevTurnRate ) )
|
||||
{
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "cancelidleloop" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
prevState = self.animSubstate;
|
||||
nextState = self DetermineState();
|
||||
if ( nextState != self.animSubstate )
|
||||
self EnterState( nextState );
|
||||
|
||||
self UpdateAngle();
|
||||
|
||||
switch ( self.animSubstate )
|
||||
{
|
||||
case "idle_combat":
|
||||
wait( 0.2 );
|
||||
break;
|
||||
case "idle_noncombat":
|
||||
if ( prevState == "none" )
|
||||
{
|
||||
//if ( self.moveMode == "run" || self.moveMode == "sprint" )
|
||||
// self PlaySoundOnMovingEnt( "anml_dog_pants_mp_fast" );
|
||||
//else
|
||||
//self PlaySoundOnMovingEnt( "anml_dog_pants_mp_med" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetTime() > self.timeOfNextSound )
|
||||
{
|
||||
//if ( RandomInt(10) < 4 )
|
||||
//self PlaySoundOnMovingEnt( "anml_doberman_bark" );
|
||||
//else
|
||||
//self PlaySoundOnMovingEnt( "anml_dog_pants_mp_med" );
|
||||
self SetTimeOfNextSound();
|
||||
}
|
||||
}
|
||||
wait ( 0.5 );
|
||||
break;
|
||||
default:
|
||||
assertmsg( "unknown dog stop state " + self.animSubstate );
|
||||
wait( 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DetermineState()
|
||||
{
|
||||
if ( ShouldAttackIdle() )
|
||||
return "idle_combat";
|
||||
else
|
||||
return "idle_noncombat";
|
||||
}
|
||||
|
||||
|
||||
EnterState( state )
|
||||
{
|
||||
self ExitState( self.animSubstate );
|
||||
self.animSubstate = state;
|
||||
|
||||
PlayIdleAnim();
|
||||
}
|
||||
|
||||
|
||||
ExitState( prevState )
|
||||
{
|
||||
if ( IsDefined( self.prevTurnRate ) )
|
||||
{
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlayIdleAnim()
|
||||
{
|
||||
if ( self.animSubstate == "idle_combat" )
|
||||
self SetAnimState( "attack_idle" );
|
||||
else
|
||||
self SetAnimState( "casual_idle" );
|
||||
}
|
||||
|
||||
|
||||
UpdateAngle()
|
||||
{
|
||||
faceTarget = undefined;
|
||||
if ( IsDefined( self.enemy ) && DistanceSquared( self.enemy.origin, self.origin ) < 1024 * 1024 )
|
||||
faceTarget = self.enemy;
|
||||
else if ( IsDefined( self.owner ) && DistanceSquared( self.owner.origin, self.origin ) > 24 * 24 )
|
||||
faceTarget = self.owner;
|
||||
|
||||
if ( IsDefined( faceTarget ) )
|
||||
{
|
||||
meToTarget = faceTarget.origin - self.origin;
|
||||
meToTargetAngles = VectorToAngles( meToTarget );
|
||||
|
||||
if ( abs( AngleClamp180( meToTargetAngles[1] - self.angles[1] ) ) > 1 )
|
||||
self TurnToAngle( meToTargetAngles[1] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ShouldAttackIdle()
|
||||
{
|
||||
return isdefined( self.enemy )
|
||||
&& maps\mp\_utility::IsReallyAlive( self.enemy )
|
||||
&& distanceSquared( self.origin, self.enemy.origin ) < 1000000;
|
||||
//&& self SeeRecently( self.enemy, 5 );
|
||||
}
|
||||
|
||||
GetTurnAnimState( angleDiff )
|
||||
{
|
||||
if ( self ShouldAttackIdle() )
|
||||
{
|
||||
if ( angleDiff < -135 || angleDiff > 135 )
|
||||
return "attack_turn_180";
|
||||
else if ( angleDiff < 0 )
|
||||
return "attack_turn_right_90";
|
||||
else
|
||||
return "attack_turn_left_90";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( angleDiff < -135 || angleDiff > 135 )
|
||||
return "casual_turn_180";
|
||||
else if ( angleDiff < 0 )
|
||||
return "casual_turn_right_90";
|
||||
else
|
||||
return "casual_turn_left_90";
|
||||
}
|
||||
}
|
||||
|
||||
TurnToAngle( desiredAngle )
|
||||
{
|
||||
currentAngle = self.angles[1];
|
||||
angleDiff = AngleClamp180( desiredAngle - currentAngle );
|
||||
|
||||
if ( -0.5 < angleDiff && angleDiff < 0.5 )
|
||||
return;
|
||||
|
||||
if ( -10 < angleDiff && angleDiff < 10 )
|
||||
{
|
||||
RotateToAngle( desiredAngle, 2 );
|
||||
return;
|
||||
}
|
||||
|
||||
animState = GetTurnAnimState( angleDiff );
|
||||
|
||||
turnAnim = self GetAnimEntry( animState, 0 );
|
||||
|
||||
animLength = GetAnimLength( turnAnim );
|
||||
animAngleDelta = GetAngleDelta3D( turnAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim angle delta" );
|
||||
|
||||
if ( AnimHasNotetrack( turnAnim, "turn_begin" ) && AnimHasNotetrack( turnAnim, "turn_end" ) )
|
||||
{
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "turn_in_place" );
|
||||
|
||||
beginTimes = GetNotetrackTimes( turnAnim, "turn_begin" );
|
||||
endTimes = GetNotetrackTimes( turnAnim, "turn_end" );
|
||||
turnTime = (endTimes[0] - beginTimes[0]) * animLength;
|
||||
|
||||
turnAdjust = AngleClamp180( angleDiff - animAngleDelta[1] );
|
||||
|
||||
turnSpeed = abs(turnAdjust) / turnTime / 20;
|
||||
turnSpeed = turnSpeed * 3.14159 / 180; // radians per frame.
|
||||
|
||||
angles = ( 0, AngleClamp180( self.angles[1] + turnAdjust ), 0 );
|
||||
|
||||
self.prevTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( turnSpeed );
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
self WaitUntilNotetrack( "turn_in_place", "turn_end" );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
|
||||
self WaitUntilNotetrack( "turn_in_place", "end" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.prevTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
|
||||
turnSpeed = abs( AngleClamp180(angleDiff-animAngleDelta[1]) ) / animLength / 20;
|
||||
turnSpeed = turnSpeed * 3.14159 / 180;
|
||||
self ScrAgentSetMaxTurnSpeed( turnSpeed ); // radians per frame.
|
||||
|
||||
angles = ( 0, AngleClamp180( desiredAngle - animAngleDelta[1] ), 0 );
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "turn_in_place" );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
self PlayIdleAnim();
|
||||
}
|
||||
|
||||
RotateToAngle( desiredAngle, tolerance )
|
||||
{
|
||||
if ( abs( AngleClamp180( desiredAngle - self.angles[1] ) ) <= tolerance )
|
||||
return;
|
||||
|
||||
angles = ( 0, desiredAngle, 0 );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
while ( AngleClamp180( desiredAngle - self.angles[1] ) > tolerance )
|
||||
wait ( 0.1 );
|
||||
}
|
||||
|
||||
SetTimeOfNextSound()
|
||||
{
|
||||
self.timeOfNextSound = GetTime() + 8000 + RandomInt( 5000 );
|
||||
}
|
||||
|
||||
DoHitReaction( hitAngle )
|
||||
{
|
||||
self.bLockGoalPos = true;
|
||||
self.stateLocked = true;
|
||||
self.bIdleHitReaction = true;
|
||||
|
||||
// hitAngle is angle from me to damage
|
||||
angleDiff = AngleClamp180( hitAngle - self.angles[1] );
|
||||
|
||||
if ( angleDiff > 0 )
|
||||
animIndex = 1; // left
|
||||
else
|
||||
animIndex = 0; // right
|
||||
|
||||
self notify( "cancelidleloop" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( "stand_pain", animIndex, "stand_pain" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self.stateLocked = false;
|
||||
self.bIdleHitReaction = false;
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.animSubstate = "none";
|
||||
self thread UpdateState();
|
||||
}
|
||||
|
||||
OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( self.bIdleHitReaction )
|
||||
return;
|
||||
|
||||
hitDirToAngles = VectorToAngles( vDir );
|
||||
hitAngle = hitDirToAngles[1] - 180;
|
||||
|
||||
self DoHitReaction( hitAngle );
|
||||
}
|
||||
|
||||
OnFlashbanged( origin, percent_distance, percent_angle, attacker, teamName, extraDuration )
|
||||
{
|
||||
if ( self.bIdleHitReaction )
|
||||
return;
|
||||
|
||||
DoHitReaction( self.angles[1] + 180 );
|
||||
}
|
350
raw/maps/mp/agents/dog/_dog_melee.gsc
Normal file
350
raw/maps/mp/agents/dog/_dog_melee.gsc
Normal file
@ -0,0 +1,350 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
|
||||
assert( IsDefined( self.curMeleeTarget ) );
|
||||
|
||||
self.curMeleeTarget endon( "disconnect" );
|
||||
|
||||
// get desired end pos.
|
||||
meToTarget = self.curMeleeTarget.origin - self.origin;
|
||||
distMeToTarget = Length( meToTarget );
|
||||
|
||||
bTestCanMove = true;
|
||||
if ( distMeToTarget < self.attackOffset )
|
||||
{
|
||||
attackPos = self.origin;
|
||||
bTestCanMove = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = meToTarget / distMeToTarget;
|
||||
attackPos = self.curMeleeTarget.origin - meToTarget * self.attackOffset;
|
||||
}
|
||||
|
||||
bLerp = false;
|
||||
|
||||
startPos = self.origin + (0,0,30);
|
||||
endPos = self.curMeleeTarget.origin + (0,0,30);
|
||||
hitPos = PhysicsTrace( startPos, endPos );
|
||||
if ( DistanceSquared( hitPos, endPos ) > 1 )
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( bTestCanMove )
|
||||
bCanMoveToAttackPos = self CanMovePointToPoint( self.origin, attackPos );
|
||||
else
|
||||
bCanMoveToAttackPos = true;
|
||||
|
||||
animEntry = undefined;
|
||||
if ( !bCanMoveToAttackPos )
|
||||
{
|
||||
bShouldDoExtendedKill = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
animEntry = self ShouldDoExtendedKill( self.curMeleeTarget );
|
||||
bShouldDoExtendedKill = IsDefined( animEntry );
|
||||
}
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
if ( bShouldDoExtendedKill )
|
||||
{
|
||||
assert( IsDefined( animEntry ) );
|
||||
self DoExtendedKill( animEntry );
|
||||
}
|
||||
else
|
||||
{
|
||||
self DoStandardKill( attackPos, bCanMoveToAttackPos );
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetMeleeAnimState()
|
||||
{
|
||||
return "attack_run_and_jump";
|
||||
}
|
||||
|
||||
// returns kill direction, if any.
|
||||
ShouldDoExtendedKill( victim )
|
||||
{
|
||||
if( !self.enableExtendedKill )
|
||||
return undefined;
|
||||
|
||||
cMaxHeightDiff = 4;
|
||||
|
||||
if ( !IsGameParticipant( victim ) ) // humans only.
|
||||
return undefined;
|
||||
if ( self IsProtectedByRiotshield( victim ) )
|
||||
return undefined;
|
||||
if ( victim IsJuggernaut() )
|
||||
return undefined;
|
||||
|
||||
victimToMe = self.origin - victim.origin;
|
||||
if ( abs( victimToMe[2] ) > cMaxHeightDiff )
|
||||
return undefined;
|
||||
|
||||
victimToMe2D = VectorNormalize( (victimToMe[0], victimToMe[1], 0) );
|
||||
victimFacing = AnglesToForward( victim.angles );
|
||||
angleToMe = VectorDot( victimFacing, victimToMe2D );
|
||||
|
||||
if ( angleToMe > 0.707 )
|
||||
{
|
||||
animEntry = 0; // front
|
||||
snappedVictimToMe = RotateVector( ( 1, 0, 0 ), victim.angles );
|
||||
}
|
||||
else if ( angleToMe < -0.707 )
|
||||
{
|
||||
animEntry = 1; // back
|
||||
snappedVictimToMe = RotateVector( (-1, 0, 0), victim.angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
cross = maps\mp\agents\dog\_dog_think::cross2D( victimToMe, victimFacing );
|
||||
if ( cross > 0 )
|
||||
{
|
||||
animEntry = 3; // right
|
||||
snappedVictimToMe = RotateVector( (0, -1, 0), victim.angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
animEntry = 2; // left
|
||||
snappedVictimToMe = RotateVector( (0, 1, 0), victim.angles );
|
||||
}
|
||||
}
|
||||
|
||||
if ( animEntry == 1 )
|
||||
cClearanceRequired = 128;
|
||||
else
|
||||
cClearanceRequired = 96;
|
||||
landPos = victim.origin - cClearanceRequired * snappedVictimToMe;
|
||||
|
||||
landPosDropped = self DropPosToGround( landPos );
|
||||
if ( !IsDefined( landPosDropped ) )
|
||||
return undefined;
|
||||
|
||||
if ( abs( landPosDropped[2] - landPos[2] ) > cMaxHeightDiff )
|
||||
return undefined;
|
||||
|
||||
if ( !self AIPhysicsTracePassed( victim.origin + (0,0,4), landPosDropped + (0,0,4), self.radius, self.height ) )
|
||||
return undefined;
|
||||
|
||||
return animEntry;
|
||||
}
|
||||
|
||||
DoExtendedKill( animEntry )
|
||||
{
|
||||
meleeAnimState = "attack_extended";
|
||||
|
||||
self DoMeleeDamage( self.curMeleeTarget, self.curMeleeTarget.health, "MOD_MELEE_DOG" );
|
||||
|
||||
attackAnim = self GetAnimEntry( meleeAnimState, animEntry );
|
||||
self thread ExtendedKill_StickToVictim( attackAnim, self.curMeleeTarget.origin, self.curMeleeTarget.angles );
|
||||
|
||||
// if ( animEntry == 1 ) // back
|
||||
// self PlaySoundOnMovingEnt( "mp_dog_attack_quick_back_npc" );
|
||||
// else
|
||||
// self PlaySoundOnMovingEnt( "mp_dog_attack_short_npc" );
|
||||
|
||||
self PlayAnimNUntilNotetrack( meleeAnimState, animEntry, "attack", "end" );
|
||||
|
||||
self notify( "kill_stick" );
|
||||
self.curMeleeTarget = undefined;
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self Unlink();
|
||||
}
|
||||
|
||||
ExtendedKill_StickToVictim( attackAnim, targetOrigin, targetAngles )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
self endon( "kill_stick" );
|
||||
|
||||
wait( 0.05 ); // must wait for anim to kick in before we can properly calculate our offsets.
|
||||
assert( IsDefined( self.curMeleeTarget ) );
|
||||
|
||||
if ( IsAlive( self.curMeleeTarget ) ) // godmode, etc.
|
||||
return;
|
||||
|
||||
corpse = self.curMeleeTarget GetCorpseEntity();
|
||||
assert( IsDefined( corpse ) );
|
||||
self LinkTo( corpse );
|
||||
|
||||
self ScrAgentDoAnimRelative( attackAnim, targetOrigin, targetAngles );
|
||||
}
|
||||
|
||||
DoStandardKill( attackPos, bCanMoveToAttackPos )
|
||||
{
|
||||
meleeAnimState = self GetMeleeAnimState();
|
||||
|
||||
bLerp = false;
|
||||
|
||||
if ( !bCanMoveToAttackPos )
|
||||
{
|
||||
if ( self AgentCanSeeSentient( self.curMeleeTarget ) )
|
||||
{
|
||||
groundPos = self DropPosToGround( self.curMeleeTarget.origin );
|
||||
if ( IsDefined( groundPos ) )
|
||||
{
|
||||
bLerp = true;
|
||||
attackPos = groundPos; // i'm going to clip the heck through him, but i need a guaranteed safe spot.
|
||||
}
|
||||
else
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.lastMeleeFailedMyPos = undefined;
|
||||
self.lastMeleeFailedPos = undefined;
|
||||
|
||||
attackAnim = self GetAnimEntry( meleeAnimState, 0 );
|
||||
animLength = GetAnimLength( attackAnim );
|
||||
meleeNotetracks = GetNotetrackTimes( attackAnim, "dog_melee" );
|
||||
if ( meleeNotetracks.size > 0 )
|
||||
lerpTime = meleeNotetracks[0] * animLength;
|
||||
else
|
||||
lerpTime = animLength;
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, attackPos, lerpTime );
|
||||
|
||||
self thread UpdateLerpPos( self.curMeleeTarget, lerpTime, bCanMoveToAttackPos );
|
||||
|
||||
self PlayAnimNUntilNotetrack( meleeAnimState, 0, "attack", "dog_melee" );
|
||||
|
||||
self notify( "cancel_updatelerppos" );
|
||||
|
||||
damageDealt = 0;
|
||||
if( IsDefined( self.curMeleeTarget ) )
|
||||
damageDealt = self.curMeleeTarget.health;
|
||||
if( IsDefined( self.meleeDamage ) )
|
||||
damageDealt = self.meleeDamage;
|
||||
|
||||
if( IsDefined( self.curMeleeTarget ) )
|
||||
self DoMeleeDamage( self.curMeleeTarget, damageDealt, "MOD_IMPACT" );
|
||||
|
||||
self.curMeleeTarget = undefined; // dude's dead now, or soon will be.
|
||||
|
||||
if ( bLerp )
|
||||
self ScrAgentSetAnimScale( 0, 1 );
|
||||
else
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
self WaitUntilNotetrack( "attack", "end" );
|
||||
}
|
||||
|
||||
UpdateLerpPos( enemy, lerpTime, bCanMoveToAttackPos )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "death" );
|
||||
self endon( "cancel_updatelerppos" );
|
||||
enemy endon( "disconnect" );
|
||||
enemy endon( "death" );
|
||||
|
||||
timeRemaining = lerpTime;
|
||||
interval = 0.05;
|
||||
while ( true )
|
||||
{
|
||||
wait( interval );
|
||||
timeRemaining -= interval;
|
||||
|
||||
if ( timeRemaining <= 0 )
|
||||
break;
|
||||
|
||||
attackPos = GetUpdatedAttackPos( enemy, bCanMoveToAttackPos );
|
||||
if ( !IsDefined( attackPos ) )
|
||||
break;
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, attackPos, timeRemaining );
|
||||
}
|
||||
}
|
||||
|
||||
GetUpdatedAttackPos( enemy, bCanMove )
|
||||
{
|
||||
if ( !bCanMove )
|
||||
{
|
||||
droppedPos = self DropPosToGround( enemy.origin );
|
||||
return droppedPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = enemy.origin - self.origin;
|
||||
distMeToTarget = Length( meToTarget );
|
||||
|
||||
if ( distMeToTarget < self.attackOffset )
|
||||
{
|
||||
return self.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = meToTarget / distMeToTarget;
|
||||
attackPos = enemy.origin - meToTarget * self.attackOffset;
|
||||
if ( self CanMovePointToPoint( self.origin, attackPos ) )
|
||||
return attackPos;
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IsProtectedByRiotshield( enemy )
|
||||
{
|
||||
if ( enemy maps\mp\_riotshield::hasRiotShield() )
|
||||
{
|
||||
enemyToMe = self.origin - enemy.origin;
|
||||
meToEnemy = VectorNormalize( ( enemyToMe[0], enemyToMe[1], 0 ) );
|
||||
|
||||
enemyFacing = AnglesToForward( enemy.angles );
|
||||
angleToMe = VectorDot( enemyFacing, enemyToMe );
|
||||
|
||||
if ( enemy maps\mp\_riotshield::hasRiotShieldEquipped() )
|
||||
{
|
||||
if ( angleToMe > 0.766 )
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( angleToMe < -0.766 )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DoMeleeDamage( enemy, damage, meansOfDeath )
|
||||
{
|
||||
if ( self IsProtectedByRiotshield( enemy ) )
|
||||
return;
|
||||
|
||||
enemy DoDamage( damage, self.origin, self, self, meansOfDeath );
|
||||
}
|
||||
|
||||
MeleeFailed()
|
||||
{
|
||||
self.lastMeleeFailedMyPos = self.origin;
|
||||
self.lastMeleeFailedPos = self.curMeleeTarget.origin;
|
||||
}
|
490
raw/maps/mp/agents/dog/_dog_move.gsc
Normal file
490
raw/maps/mp/agents/dog/_dog_move.gsc
Normal file
@ -0,0 +1,490 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
|
||||
self StartMove();
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
self CancelAllBut( undefined );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
|
||||
SetupMovement()
|
||||
{
|
||||
self thread WaitForRunWalkChange();
|
||||
self thread WaitForSharpTurn();
|
||||
self thread WaitForStop();
|
||||
//self thread WaitForStopEarly();
|
||||
//self thread HandleMoveLoopFootsteps();
|
||||
}
|
||||
|
||||
ContinueMovement()
|
||||
{
|
||||
self SetupMovement();
|
||||
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self SetMoveAnim( self.moveMode );
|
||||
}
|
||||
|
||||
SetMoveAnim( moveMode )
|
||||
{
|
||||
self SetAnimState( moveMode );
|
||||
}
|
||||
|
||||
WaitForRunWalkChange()
|
||||
{
|
||||
self endon( "dogmove_endwait_runwalk" );
|
||||
curMovement = self.moveMode;
|
||||
while ( true )
|
||||
{
|
||||
if ( curMovement != self.moveMode )
|
||||
{
|
||||
self SetMoveAnim( self.moveMode );
|
||||
curMovement = self.moveMode;
|
||||
}
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
DoSharpTurn( newDir )
|
||||
{
|
||||
lookaheadAngles = VectorToAngles( newDir );
|
||||
angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
|
||||
if ( angleIndex == 4 ) // 4 means this turn wasn't sharp enough for me to care. (angle ~= 0)
|
||||
{
|
||||
ContinueMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
animState = "sharp_turn";
|
||||
|
||||
turnAnim = self GetAnimEntry( animState, angleIndex );
|
||||
animAngleDelta = GetAngleDelta( turnAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - animAngleDelta ), 0 ) );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, angleIndex, "sharp_turn" );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForSharpTurn()
|
||||
{
|
||||
self endon( "dogmove_endwait_sharpturn" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
self CancelAllBut( "sharpturn" );
|
||||
|
||||
self DoSharpTurn( newDir );
|
||||
}
|
||||
|
||||
WaitForStop()
|
||||
{
|
||||
self endon( "dogmove_endwait_stop" );
|
||||
|
||||
self waittill( "stop_soon" );
|
||||
|
||||
if ( IsDefined( self.bArrivalsEnabled ) && !self.bArrivalsEnabled )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopState = self GetStopAnimState();
|
||||
|
||||
stopAnim = self GetAnimEntry( stopState.state, stopState.index );
|
||||
stopDelta = GetMoveDelta( stopAnim );
|
||||
stopAngleDelta = GetAngleDelta( stopAnim );
|
||||
|
||||
goalPos = self GetPathGoalPos();
|
||||
assert( IsDefined( goalPos ) );
|
||||
|
||||
meToStop = goalPos - self.origin;
|
||||
// not enough room left to play the animation. abort. (i'm willing to squish/scale the anim up to 12 units.)
|
||||
if ( Length( meToStop ) + 12 < Length( stopDelta ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopData = self GetStopData();
|
||||
stopStartPos = self CalcAnimStartPos( stopData.pos, stopData.angles[1], stopDelta, stopAngleDelta );
|
||||
stopStartPosDropped = DropPosToGround( stopStartPos );
|
||||
|
||||
if ( !IsDefined( stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !self CanMovePointToPoint( stopData.pos, stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
self CancelAllBut( "stop" );
|
||||
|
||||
self thread WaitForPathSetWhileStopping();
|
||||
self thread WaitForSharpTurnWhileStopping();
|
||||
if ( DistanceSquared( stopStartPos, self.origin ) > 4 )
|
||||
{
|
||||
self ScrAgentSetWaypoint( stopStartPos );
|
||||
self thread WaitForBlockedWhileStopping();
|
||||
self waittill( "waypoint_reached" );
|
||||
self notify( "dogmove_endwait_blockedwhilestopping" );
|
||||
}
|
||||
|
||||
facingDir = goalPos - self.origin;
|
||||
facingAngles = VectorToAngles( facingDir );
|
||||
facingYaw = ( 0, facingAngles[1] - stopAngleDelta, 0 );
|
||||
|
||||
// scale the anim if necessary, to make sure we end up where we wanted to end up.
|
||||
scaleFactors = GetAnimScaleFactors( goalPos - self.origin, stopDelta );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", facingYaw, ( 0, facingAngles[1], 0 ) );
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( stopState.state, stopState.index, "move_stop" );
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin ); // whether i got where i was going, get where i got.
|
||||
}
|
||||
|
||||
WaitForPathSetWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_pathsetwhilestopping" );
|
||||
|
||||
oldGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
self waittill( "path_set" );
|
||||
|
||||
newGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
if ( DistanceSquared( oldGoalPos, newGoalPos ) < 1 )
|
||||
{
|
||||
self thread WaitForPathSetWhileStopping();
|
||||
return;
|
||||
}
|
||||
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
self notify( "dogmove_endwait_sharpturnwhilestopping" );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForSharpTurnWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_sharpturnwhilestopping" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
|
||||
self notify( "dogmove_endwait_pathsetwhilestopping" );
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
|
||||
self DoSharpTurn( newDir );
|
||||
}
|
||||
|
||||
WaitForBlockedWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_blockedwhilestopping" );
|
||||
|
||||
self waittill( "path_blocked" );
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
self ScrAgentSetWaypoint( undefined );
|
||||
}
|
||||
|
||||
WaitForStopEarly()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_stopearly" );
|
||||
|
||||
stopAnim = self GetAnimEntry( "move_stop_4", 0 );
|
||||
stopAnimTranslation = GetMoveDelta( stopAnim );
|
||||
stoppingDistance = Length( stopAnimTranslation );
|
||||
offset = self.preferredOffsetFromOwner + stoppingDistance;
|
||||
offsetSq = offset * offset;
|
||||
|
||||
if ( DistanceSquared( self.origin, self.owner.origin ) <= offsetSq )
|
||||
return;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( !IsDefined( self.owner ) )
|
||||
break;
|
||||
|
||||
if ( DistanceSquared( self.origin, self.owner.origin ) < offsetSq )
|
||||
{
|
||||
stopPos = self LocalToWorldCoords( stopAnimTranslation );
|
||||
self ScrAgentSetGoalPos( stopPos );
|
||||
break;
|
||||
}
|
||||
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
CancelAllBut( doNotCancel )
|
||||
{
|
||||
cleanups = [ "runwalk", "sharpturn", "stop", "pathsetwhilestopping", "blockedwhilestopping", "sharpturnwhilestopping", "stopearly" ];
|
||||
|
||||
bCheckDoNotCancel = IsDefined( doNotCancel );
|
||||
|
||||
foreach ( cleanup in cleanups )
|
||||
{
|
||||
if ( bCheckDoNotCancel && cleanup == doNotCancel )
|
||||
continue;
|
||||
self notify( "dogmove_endwait_" + cleanup );
|
||||
}
|
||||
}
|
||||
|
||||
StartMove()
|
||||
{
|
||||
negStartNode = self GetNegotiationStartNode();
|
||||
if ( IsDefined( negStartNode ) )
|
||||
goalPos = negStartNode.origin;
|
||||
else
|
||||
goalPos = self GetPathGoalPos();
|
||||
|
||||
// don't play start if i have no room for the start.
|
||||
if ( DistanceSquared( goalPos, self.origin ) < 100 * 100 )
|
||||
return;
|
||||
|
||||
lookaheadDir = self GetLookaheadDir();
|
||||
lookaheadAngles = VectorToAngles( lookaheadDir );
|
||||
|
||||
myVelocity = self GetVelocity();
|
||||
if ( Length2DSquared( myVelocity ) > 16 )
|
||||
{
|
||||
myVelocity = VectorNormalize( myVelocity );
|
||||
if ( VectorDot( myVelocity, lookaheadDir ) > 0.707 )
|
||||
return; // don't need a start if i'm already moving in the direction i want to move.
|
||||
}
|
||||
|
||||
angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
|
||||
startAnim = self GetAnimEntry( "move_start", angleIndex );
|
||||
startAnimTranslation = GetMoveDelta( startAnim );
|
||||
|
||||
endPos = RotateVector( startAnimTranslation, self.angles ) + self.origin;
|
||||
if ( !self CanMovePointToPoint( self.origin, endPos ) )
|
||||
return;
|
||||
|
||||
startAnimAngles = GetAngleDelta3D( startAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
if ( 3 <= angleIndex && angleIndex <= 5 )
|
||||
self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - startAnimAngles[1] ), 0 ) );
|
||||
else
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
self PlayAnimNUntilNotetrack( "move_start", angleIndex, "move_start" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetStopData()
|
||||
{
|
||||
stopData = SpawnStruct();
|
||||
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
stopData.pos = self.node.origin;
|
||||
stopData.angles = self.node.angles;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathGoalPos = self GetPathGoalPos();
|
||||
assert( IsDefined( pathGoalPos ) );
|
||||
stopData.pos = pathGoalPos;
|
||||
//stopData.angles = self.angles;
|
||||
stopData.angles = VectorToAngles( self GetLookaheadDir() );
|
||||
}
|
||||
|
||||
return stopData;
|
||||
}
|
||||
|
||||
GetStopAnimState( angle )
|
||||
{
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
angleDiff = self.node.angles[1] - self.angles[1];
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
}
|
||||
else
|
||||
{
|
||||
angleIndex = 4;
|
||||
}
|
||||
|
||||
result = SpawnStruct();
|
||||
result.state = "move_stop";
|
||||
result.index = angleIndex;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CalcAnimStartPos( stopPos, stopAngle, animDelta, animAngleDelta )
|
||||
{
|
||||
dAngle = stopAngle - animAngleDelta;
|
||||
angles = ( 0, dAngle, 0 );
|
||||
vForward = AnglesToForward( angles );
|
||||
vRight = AnglesToRight( angles );
|
||||
|
||||
forward = vForward * animDelta[0];
|
||||
right = vRight * animDelta[1];
|
||||
|
||||
return stopPos - forward + right;
|
||||
}
|
||||
|
||||
Dog_AddLean()
|
||||
{
|
||||
leanFrac = Clamp( self.leanAmount / 25.0, -1, 1 );
|
||||
if ( leanFrac > 0 )
|
||||
{
|
||||
// set lean left( leanFrac );
|
||||
// set lean right( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// set lean left( 0 );
|
||||
// set lean right( 0 - leanFrac );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HandleFootstepNotetracks( note, animState, animIndex, animTime )
|
||||
{
|
||||
if ( true )
|
||||
return false;
|
||||
|
||||
switch ( note )
|
||||
{
|
||||
case "footstep_front_left_small":
|
||||
case "footstep_front_right_small":
|
||||
case "footstep_back_left_small":
|
||||
case "footstep_back_right_small":
|
||||
case "footstep_front_left_large":
|
||||
case "footstep_front_right_large":
|
||||
case "footstep_back_left_large":
|
||||
case "footstep_back_right_large":
|
||||
{
|
||||
surfaceType = undefined;
|
||||
if ( IsDefined( self.surfaceType ) )
|
||||
{
|
||||
surfaceType = self.surfaceType;
|
||||
self.lastSurfaceType = surfaceType;
|
||||
}
|
||||
else if ( IsDefined( self.lastSurfaceType ) )
|
||||
{
|
||||
surfaceType = self.lastSurfaceType;
|
||||
}
|
||||
else
|
||||
{
|
||||
surfaceType = "dirt";
|
||||
}
|
||||
|
||||
if ( surfaceType != "dirt" && surfaceType != "concrete" && surfaceType != "wood" && surfaceType != "metal" )
|
||||
surfaceType = "dirt";
|
||||
|
||||
if ( surfaceType == "concrete" ) // code == concrete, sound == cement.
|
||||
surfaceType = "cement";
|
||||
|
||||
//moveType = self.sound_animMoveType;
|
||||
//if ( !IsDefined( moveType ) )
|
||||
// moveType = "run";
|
||||
if ( self.aiState == "traverse" )
|
||||
moveType = "land";
|
||||
else if ( self.moveMode == "sprint" )
|
||||
moveType = "sprint";
|
||||
else if ( self.moveMode == "fastwalk" )
|
||||
moveType = "walk";
|
||||
else
|
||||
moveType = "run";
|
||||
|
||||
self PlaySoundOnMovingEnt( "dogstep_" + moveType + "_" + surfaceType );
|
||||
|
||||
if ( IsSubStr( note, "front_left" ) )
|
||||
{
|
||||
soundAlias1 = "anml_dog_mvmt_accent";
|
||||
soundAlias2 = "anml_dog_mvmt_vest";
|
||||
|
||||
if ( moveType == "walk" )
|
||||
suffix = "_npc";
|
||||
else
|
||||
suffix = "_run_npc";
|
||||
|
||||
self PlaySoundOnMovingEnt( soundAlias1 + suffix );
|
||||
self PlaySoundOnMovingEnt( soundAlias2 + suffix );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DoHitReaction( hitAngle )
|
||||
{
|
||||
self CancelAllBut( undefined );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
self.stateLocked = true;
|
||||
|
||||
// hitAngle is angle from me to damage
|
||||
angleDiff = AngleClamp180( hitAngle - self.angles[1] );
|
||||
|
||||
if ( angleDiff > 0 )
|
||||
animIndex = 1; // left
|
||||
else
|
||||
animIndex = 0; // right
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( "run_pain", animIndex, "run_pain" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self.stateLocked = false;
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( self.stateLocked )
|
||||
return;
|
||||
|
||||
hitDirToAngles = VectorToAngles( vDir );
|
||||
hitAngle = hitDirToAngles[1] - 180;
|
||||
|
||||
self DoHitReaction( hitAngle );
|
||||
}
|
||||
|
||||
OnFlashbanged( origin, percent_distance, percent_angle, attacker, teamName, extraDuration )
|
||||
{
|
||||
if ( self.stateLocked )
|
||||
return;
|
||||
|
||||
DoHitReaction( self.angles[1] + 180 );
|
||||
}
|
1198
raw/maps/mp/agents/dog/_dog_think.gsc
Normal file
1198
raw/maps/mp/agents/dog/_dog_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
242
raw/maps/mp/agents/dog/_dog_traverse.gsc
Normal file
242
raw/maps/mp/agents/dog/_dog_traverse.gsc
Normal file
@ -0,0 +1,242 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
CONST_BOT_WALK_FORWARD_UNITS_PER_SECOND = 256;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
if ( !IsDefined( level.dogTraverseAnims ) )
|
||||
InitDogTraverseAnims();
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
assert( IsDefined( startNode ) && IsDefined( endNode ) );
|
||||
|
||||
if( startNode.animscript == "bot_walk_forward" )
|
||||
{
|
||||
startToEnd = endNode.origin - startNode.origin;
|
||||
lerpTime = Length(startToEnd) / CONST_BOT_WALK_FORWARD_UNITS_PER_SECOND;
|
||||
startToEnd2D = ( startToEnd[0], startToEnd[1], 0);
|
||||
anglestoEnd = VectorToAngles( startToEnd2D );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglestoEnd );
|
||||
self ScrAgentDoAnimLerp( startNode.origin, endNode.origin, lerpTime );
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self PlayAnimForTime( "run", lerpTime );
|
||||
return;
|
||||
}
|
||||
|
||||
animState = undefined;
|
||||
animState = level.dogTraverseAnims[ startNode.animscript ];
|
||||
|
||||
if ( !IsDefined( animState ) )
|
||||
{
|
||||
assertmsg( "no animation for traverse " + startNode.animscript + "@ " + startNode.origin );
|
||||
return;
|
||||
}
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
startToEnd = endNode.origin - startNode.origin;
|
||||
startToEnd2D = ( startToEnd[0], startToEnd[1], 0 );
|
||||
anglesToEnd = VectorToAngles( startToEnd2D );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToEnd );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
traverseAnim = self GetAnimEntry( animState, 0 );
|
||||
|
||||
codeMoveNotetracks = GetNotetrackTimes( traverseAnim, "code_move" );
|
||||
if ( codeMoveNotetracks.size > 0 )
|
||||
moveDelta = GetMoveDelta( traverseAnim, 0, codeMoveNotetracks[0] );
|
||||
else
|
||||
moveDelta = GetMoveDelta( traverseAnim, 0, 1 );
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( startToEnd, moveDelta );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
|
||||
// the end node is higher than the start node.
|
||||
if ( startToEnd[2] > 0 )
|
||||
{
|
||||
if ( moveDelta[2] > 0 )
|
||||
{
|
||||
jumpStartNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_start" );
|
||||
if ( jumpStartNotetracks.size > 0 )
|
||||
{
|
||||
xyScale = 1;
|
||||
zScale = 1;
|
||||
if ( Length2DSquared( startToEnd2D ) < 0.8 * 0.8 * Length2DSquared( moveDelta ) )
|
||||
xyScale = 0.4;
|
||||
if ( startToEnd[2] < 0.75 * moveDelta[2] )
|
||||
zScale = 0.5;
|
||||
|
||||
self ScrAgentSetAnimScale( xyScale, zScale );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "traverse_jump_start" );
|
||||
jumpEndNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_end" );
|
||||
assert( jumpEndNotetracks.size > 0 );
|
||||
jumpStartMoveDelta = GetMoveDelta( traverseAnim, 0, jumpStartNotetracks[0] );
|
||||
jumpEndMoveDelta = GetMoveDelta( traverseAnim, 0, jumpEndNotetracks[0] );
|
||||
|
||||
xyScale = 1;
|
||||
zScale = 1;
|
||||
currentToEnd = endNode.origin - self.origin;
|
||||
animToEnd = moveDelta - jumpStartMoveDelta;
|
||||
if ( Length2DSquared( currentToEnd ) < 0.75 * 0.75 * Length2DSquared( animToEnd ) )
|
||||
xyScale = 0.75;
|
||||
if ( currentToEnd[2] < 0.75 * animToEnd[2] )
|
||||
zScale = 0.75;
|
||||
|
||||
animJumpEndToEnd = moveDelta - jumpEndMoveDelta;
|
||||
scaledAnimJumpEndToEnd = ( animJumpEndToEnd[0] * xyScale, animJumpEndToEnd[1] * xyScale, animJumpEndToEnd[2] * zScale );
|
||||
worldAnimJumpEndToEnd = RotateVector( scaledAnimJumpEndToEnd, anglesToEnd );
|
||||
nodeJumpEndPos = endNode.origin - worldAnimJumpEndToEnd;
|
||||
|
||||
animJumpStartToJumpEnd = jumpEndMoveDelta - jumpStartMoveDelta;
|
||||
worldAnimJumpStartToJumpEnd = RotateVector( animJumpStartToJumpEnd, anglesToEnd );
|
||||
currentToNodeJumpEnd = nodeJumpEndPos - self.origin;
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( currentToNodeJumpEnd, worldAnimJumpStartToJumpEnd, true);
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self WaitUntilNotetrack( "traverse", "traverse_jump_end" );
|
||||
|
||||
self ScrAgentSetAnimScale( xyScale, zScale );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // can't do negative scale. use lerp.
|
||||
gravityOnNotetracks = GetNotetrackTimes( traverseAnim, "gravity on" );
|
||||
if ( gravityOnNotetracks.size > 0 )
|
||||
{
|
||||
targetEntPos = startNode GetTargetEntPos();
|
||||
if ( IsDefined( targetEntPos ) )
|
||||
{
|
||||
startToTarget = targetEntPos - self.origin;
|
||||
targetToEnd = endNode.origin - targetEntPos;
|
||||
|
||||
startDelta = GetMoveDelta( traverseAnim, 0, gravityOnNotetracks[0] );
|
||||
scaleFactors = self GetAnimScaleFactors( startToTarget, startDelta );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "gravity on" );
|
||||
|
||||
endDelta = GetMoveDelta( traverseAnim, gravityOnNotetracks[0], 1 );
|
||||
scaleFactors = self GetAnimScaleFactors( targetToEnd, endDelta );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
animLength = GetAnimLength( traverseAnim );
|
||||
self ScrAgentDoAnimLerp( startNode.origin, endNode.origin, animLength );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gravityOnNotetracks = GetNotetrackTimes( traverseAnim, "gravity on" );
|
||||
if ( gravityOnNotetracks.size > 0 )
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, 1 );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "gravity on" );
|
||||
|
||||
gravityOnMoveDelta = GetMoveDelta( traverseAnim, 0, gravityOnNotetracks[0] );
|
||||
zAnimDelta = gravityOnMoveDelta[2] - moveDelta[2];
|
||||
|
||||
if ( abs( zAnimDelta ) > 0 )
|
||||
{
|
||||
zMeToEnd = self.origin[2] - endNode.origin[2];
|
||||
|
||||
zScale = zMeToEnd / zAnimDelta;
|
||||
assert( zScale > 0 );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, zScale );
|
||||
|
||||
animrate = Clamp( 2 / zScale, 0.5, 1 );
|
||||
|
||||
norestart = animState + "_norestart";
|
||||
self SetAnimState( norestart, 0, animrate );
|
||||
}
|
||||
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
|
||||
animrate = Clamp( 2 / scaleFactors.z, 0.5, 1 );
|
||||
|
||||
jumpEndNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_end" );
|
||||
if ( jumpEndNotetracks.size > 0 )
|
||||
{
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, 0, animrate, "traverse", "traverse_jump_end" );
|
||||
norestart = animState + "_norestart";
|
||||
self SetAnimState( norestart, 0, 1 );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetTargetEntPos()
|
||||
{
|
||||
if ( IsDefined( self.targetEntPos ) )
|
||||
return self.targetEntPos;
|
||||
|
||||
targetEnt = GetEnt( self.target, "targetname" );
|
||||
if ( !IsDefined( targetEnt ) )
|
||||
return undefined;
|
||||
|
||||
self.targetEntPos = targetEnt.origin;
|
||||
targetEnt delete();
|
||||
return self.targetEntPos;
|
||||
}
|
||||
|
||||
InitDogTraverseAnims()
|
||||
{
|
||||
level.dogTraverseAnims = [];
|
||||
|
||||
level.dogTraverseAnims[ "hjk_tree_hop" ] = "traverse_jump_over_24";
|
||||
level.dogTraverseAnims[ "jump_across_72" ] = "traverse_jump_over_24";
|
||||
level.dogTraverseAnims[ "wall_hop" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_2" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "wall_over_40" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "wall_over" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_divethrough_36" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_over_40" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_over_quick" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "jump_up_80" ] = "traverse_jump_up_70";
|
||||
level.dogTraverseAnims[ "jump_standing_80" ] = "traverse_jump_up_70";
|
||||
level.dogTraverseAnims[ "jump_down_80" ] = "traverse_jump_down_70";
|
||||
level.dogTraverseAnims[ "jumpdown_96" ] = "traverse_jump_down_70";
|
||||
level.dogTraverseAnims[ "jump_up_40" ] = "traverse_jump_up_40";
|
||||
level.dogTraverseAnims[ "jump_down_40" ] = "traverse_jump_down_40";
|
||||
level.dogTraverseAnims[ "step_up" ] = "traverse_jump_up_24";
|
||||
level.dogTraverseAnims[ "step_up_24" ] = "traverse_jump_up_24";
|
||||
level.dogTraverseAnims[ "step_down" ] = "traverse_jump_down_24";
|
||||
level.dogTraverseAnims[ "jump_down" ] = "traverse_jump_down_24";
|
||||
level.dogTraverseAnims[ "jump_across" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "jump_across_100" ] = "traverse_jump_over_36";
|
||||
}
|
||||
|
1265
raw/maps/mp/agents/dog/_instinct_dog_think.gsc
Normal file
1265
raw/maps/mp/agents/dog/_instinct_dog_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
3865
raw/maps/mp/bots/_bots.gsc
Normal file
3865
raw/maps/mp/bots/_bots.gsc
Normal file
File diff suppressed because it is too large
Load Diff
747
raw/maps/mp/bots/_bots_gametype_ball.gsc
Normal file
747
raw/maps/mp/bots/_bots_gametype_ball.gsc
Normal file
@ -0,0 +1,747 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
/#
|
||||
SCR_CONST_BALL_BOTS_IGNORE_HUMAN_PLAYER_ROLES = false;
|
||||
SCR_CONST_BALL_OVERRIDE_ROLE = undefined;
|
||||
#/
|
||||
|
||||
SCR_CONST_BOT_BALL_OBJ_RADIUS = 180;
|
||||
SCR_CONST_BALL_NODE_MAX_DIST = 375;
|
||||
SCR_CONST_BALL_MAX_ENEMY_THROW_DIST = 350;
|
||||
|
||||
main()
|
||||
{
|
||||
// This is called directly from native code on game startup after the _bots::main() is executed
|
||||
setup_callbacks();
|
||||
setup_bot_ball();
|
||||
|
||||
/#
|
||||
thread bot_ball_debug();
|
||||
#/
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.bot_funcs["gametype_think"] = ::bot_ball_think;
|
||||
level.bot_funcs["crate_can_use"] = ::crate_can_use;
|
||||
}
|
||||
|
||||
setup_bot_ball()
|
||||
{
|
||||
/#
|
||||
if ( SCR_CONST_BALL_BOTS_IGNORE_HUMAN_PLAYER_ROLES )
|
||||
level.bot_gametype_ignore_human_player_roles = true;
|
||||
#/
|
||||
|
||||
level.bot_gametype_attacker_limit_for_team = ::bot_ball_attacker_limit_for_team;
|
||||
level.bot_gametype_defender_limit_for_team = ::bot_ball_defender_limit_for_team;
|
||||
level.bot_gametype_allied_attackers_for_team = ::get_allied_attackers_for_team;
|
||||
level.bot_gametype_allied_defenders_for_team = ::get_allied_defenders_for_team;
|
||||
|
||||
bot_waittill_bots_enabled();
|
||||
|
||||
while( !IsDefined(level.ball_goals) )
|
||||
wait(0.05);
|
||||
|
||||
level.ball_goals["allies"].script_label = "allies";
|
||||
level.ball_goals["axis"].script_label = "axis";
|
||||
|
||||
bot_setup_ball_jump_nodes();
|
||||
|
||||
zone = GetZoneNearest( level.ball_goals["allies"].origin );
|
||||
if ( IsDefined( zone ) )
|
||||
BotZoneSetTeam( zone, "allies" );
|
||||
|
||||
zone = GetZoneNearest( level.ball_goals["axis"].origin );
|
||||
if ( IsDefined( zone ) )
|
||||
BotZoneSetTeam( zone, "axis" );
|
||||
|
||||
foreach( ball in level.balls )
|
||||
ball thread monitor_ball();
|
||||
|
||||
use_override_role = false;
|
||||
|
||||
thread bot_gametype_attacker_defender_ai_director_update();
|
||||
|
||||
/#
|
||||
if ( IsDefined(SCR_CONST_BALL_OVERRIDE_ROLE) )
|
||||
{
|
||||
wait(0.1);
|
||||
level notify("bot_gametype_attacker_defender_ai_director_update");
|
||||
}
|
||||
#/
|
||||
|
||||
level.bot_gametype_precaching_done = true;
|
||||
}
|
||||
|
||||
monitor_ball()
|
||||
{
|
||||
last_origin = self.visuals[0].origin;
|
||||
self.nearest_node = GetClosestNodeInSight(last_origin);
|
||||
while(1)
|
||||
{
|
||||
cur_origin = self.visuals[0].origin;
|
||||
self.ball_at_rest = bot_vectors_are_equal(last_origin,cur_origin);
|
||||
|
||||
if ( !self.ball_at_rest )
|
||||
{
|
||||
nearest_node = GetClosestNodeInSight( cur_origin );
|
||||
if ( !IsDefined(nearest_node) )
|
||||
{
|
||||
nodes = GetNodesInRadiusSorted( cur_origin, 512, 0, 6000 );
|
||||
if ( nodes.size > 0 )
|
||||
nearest_node = nodes[0];
|
||||
}
|
||||
|
||||
if ( IsDefined(nearest_node) )
|
||||
self.nearest_node = nearest_node;
|
||||
}
|
||||
|
||||
last_origin = cur_origin;
|
||||
|
||||
wait(0.2);
|
||||
}
|
||||
}
|
||||
|
||||
bot_setup_ball_jump_nodes()
|
||||
{
|
||||
wait(1.0);
|
||||
|
||||
num_traces = 0;
|
||||
increment = 10; // do 10 traces per frame
|
||||
|
||||
foreach( goal in level.ball_goals )
|
||||
{
|
||||
goal.ball_jump_nodes = [];
|
||||
|
||||
nodes = GetNodesInRadius( goal.origin, SCR_CONST_BALL_NODE_MAX_DIST, 0 );
|
||||
foreach( node in nodes )
|
||||
{
|
||||
if ( node.type == "End" )
|
||||
continue;
|
||||
|
||||
num_traces++;
|
||||
if ( bot_ball_origin_can_see_goal( node.origin, goal, true ) )
|
||||
goal.ball_jump_nodes[goal.ball_jump_nodes.size] = node;
|
||||
|
||||
if ( (num_traces % increment) == 0 )
|
||||
wait(0.05);
|
||||
}
|
||||
|
||||
nearest_node_2d_dist_sq = 999999999;
|
||||
foreach( node in goal.ball_jump_nodes )
|
||||
{
|
||||
dist_2d_sq_to_node = Distance2DSquared( node.origin, goal.origin );
|
||||
if ( dist_2d_sq_to_node < nearest_node_2d_dist_sq )
|
||||
{
|
||||
goal.nearest_node = node;
|
||||
nearest_node_2d_dist_sq = dist_2d_sq_to_node;
|
||||
}
|
||||
}
|
||||
|
||||
AssertEx( goal.ball_jump_nodes.size > 0, "Uplink goal at " + goal.origin + " needs pathnodes within a " + SCR_CONST_BALL_NODE_MAX_DIST + " unit radius with sight to the goal" );
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
bot_ball_origin_can_see_goal( origin, goal, thorough )
|
||||
{
|
||||
trace_succeeded = self bot_ball_trace_to_origin( origin, goal.origin );
|
||||
if ( IsDefined(thorough) && thorough )
|
||||
{
|
||||
if ( !trace_succeeded )
|
||||
{
|
||||
goal_origin = goal.origin - (0,0,goal.radius * 0.5);
|
||||
trace_succeeded = self bot_ball_trace_to_origin( origin, goal_origin );
|
||||
}
|
||||
|
||||
if ( !trace_succeeded )
|
||||
{
|
||||
goal_origin = goal.origin + (0,0,goal.radius * 0.5);
|
||||
trace_succeeded = self bot_ball_trace_to_origin( origin, goal_origin );
|
||||
}
|
||||
}
|
||||
|
||||
return trace_succeeded;
|
||||
}
|
||||
|
||||
bot_ball_trace_to_origin( start_origin, end_origin )
|
||||
{
|
||||
if ( IsDefined(self) && (IsPlayer(self) || IsAgent(self)) )
|
||||
hitPos = PlayerPhysicsTrace( start_origin, end_origin, self );
|
||||
else
|
||||
hitPos = PlayerPhysicsTrace( start_origin, end_origin );
|
||||
return (DistanceSquared( hitPos, end_origin ) < 1);
|
||||
}
|
||||
|
||||
/#
|
||||
bot_ball_debug()
|
||||
{
|
||||
while( !IsDefined(level.bot_gametype_precaching_done) )
|
||||
wait(0.05);
|
||||
|
||||
while(1)
|
||||
{
|
||||
if ( GetDvarInt("bot_DrawDebugGametype") == 1 )
|
||||
{
|
||||
foreach( goal in level.ball_goals )
|
||||
{
|
||||
foreach( node in goal.ball_jump_nodes )
|
||||
{
|
||||
color = undefined;
|
||||
if ( node == goal.nearest_node )
|
||||
color = (0,1,0);
|
||||
else if ( goal.team == "allies" )
|
||||
color = (0,0,1);
|
||||
else if ( goal.team == "axis" )
|
||||
color = (1,0,0);
|
||||
|
||||
bot_draw_cylinder( node.origin, 10, 10, 0.05, undefined, color, true, 4);
|
||||
Line( goal.origin, node.origin, color, 1.0, true );
|
||||
}
|
||||
}
|
||||
|
||||
foreach( ball in level.balls )
|
||||
{
|
||||
if ( !IsDefined(ball.carrier) )
|
||||
{
|
||||
bot_draw_cylinder( ball.nearest_node.origin, 10, 10, 0.05, undefined, (0,1,0), true, 4 );
|
||||
Line( ball bot_ball_get_origin(), ball.nearest_node.origin, (0,1,0), 1.0, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
crate_can_use( crate )
|
||||
{
|
||||
// Agents can only pickup boxes normally
|
||||
if ( IsAgent(self) && !IsDefined( crate.boxType ) )
|
||||
return false;
|
||||
|
||||
if ( self has_ball() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bot_ball_think()
|
||||
{
|
||||
self notify( "bot_ball_think" );
|
||||
self endon( "bot_ball_think" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
while( !IsDefined(level.bot_gametype_precaching_done) )
|
||||
wait(0.05);
|
||||
|
||||
self BotSetFlag("separation",0); // don't slow down when we get close to other bots
|
||||
|
||||
can_predict_carrier_loc = RandomInt(100) < (self BotGetDifficultySetting("strategyLevel") * 25);
|
||||
next_predict_carrier_location_time = 0;
|
||||
self.last_pass_throw_check = 0;
|
||||
self.ball_can_pass_ally = RandomInt(100) < (self BotGetDifficultySetting("strategyLevel") * 25);
|
||||
self.ball_can_pass_enemy = RandomInt(100) < (self BotGetDifficultySetting("strategyLevel") * 25);
|
||||
self.ball_can_throw = RandomInt(100) < (self BotGetDifficultySetting("strategyLevel") * 25);
|
||||
|
||||
my_goal = level.ball_goals[self.team];
|
||||
enemy_goal = level.ball_goals[get_enemy_team(self.team)];
|
||||
|
||||
self childthread watch_ball_pickup_and_loss();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( self.health <= 0 )
|
||||
continue;
|
||||
|
||||
needs_new_role = !IsDefined(self.role);
|
||||
/#
|
||||
if ( needs_new_role && IsDefined(SCR_CONST_BALL_OVERRIDE_ROLE) )
|
||||
{
|
||||
needs_new_role = false;
|
||||
self.role = SCR_CONST_BALL_OVERRIDE_ROLE;
|
||||
}
|
||||
#/
|
||||
if ( needs_new_role )
|
||||
self bot_gametype_initialize_attacker_defender_role();
|
||||
|
||||
self BotSetFlag( "force_sprint", false );
|
||||
|
||||
ally_balls_carried = bot_ball_get_balls_carried_by_team(self.team);
|
||||
enemy_balls_carried = bot_ball_get_balls_carried_by_team(get_enemy_team(self.team));
|
||||
|
||||
foreach( ball in enemy_balls_carried )
|
||||
{
|
||||
ball_carried_origin = ball bot_ball_get_origin() - (0,0,75);
|
||||
self BotGetImperfectEnemyInfo( ball.carrier, ball_carried_origin );
|
||||
}
|
||||
|
||||
if ( self has_ball() )
|
||||
{
|
||||
// I have the ball, so bring it home
|
||||
|
||||
self BotSetFlag( "force_sprint", true );
|
||||
|
||||
// First pick a random close jump node
|
||||
distance_to_goal_sq = DistanceSquared(self.origin, enemy_goal.nearest_node.origin);
|
||||
if ( distance_to_goal_sq > 600 * 600 )
|
||||
{
|
||||
goal_jump_node_picked = enemy_goal.nearest_node;
|
||||
goal_radius = 600;
|
||||
}
|
||||
else
|
||||
{
|
||||
goal_jump_node_picked = get_array_of_closest(self.origin, enemy_goal.ball_jump_nodes)[0];
|
||||
goal_radius = 16;
|
||||
}
|
||||
|
||||
self clear_defend_or_goal_if_necessary();
|
||||
self BotSetScriptGoal( goal_jump_node_picked.origin, goal_radius, "critical" );
|
||||
result = self bot_waittill_goal_or_fail(undefined,"bot_no_longer_has_ball");
|
||||
|
||||
if ( result == "goal" && DistanceSquared( self.origin, goal_jump_node_picked.origin ) <= 16*16 )
|
||||
{
|
||||
// Made it to the jump node chosen. Now, need to make the jump into the goal
|
||||
self BotClearScriptGoal();
|
||||
look_dir = VectorNormalize(enemy_goal.origin - self GetEye());
|
||||
if ( VectorDot(look_dir,(0,0,1)) < 0.93 )
|
||||
self BotLookAtPoint(enemy_goal.origin, 5.0, "script_forced");
|
||||
|
||||
time_waited = 0;
|
||||
has_dodged_toward_goal = false;
|
||||
last_height = self.origin[2];
|
||||
while( time_waited < 4.0 && self has_ball() )
|
||||
{
|
||||
self BotSetScriptMove( VectorToYaw(enemy_goal.origin - self.origin), 0.05 );
|
||||
if ( time_waited == 0.30 || time_waited == 0.75 )
|
||||
self BotPressButton("jump");
|
||||
|
||||
reached_jump_apex = (time_waited > 1.25) && (self.origin[2] < last_height);
|
||||
last_height = self.origin[2];
|
||||
if ( !has_dodged_toward_goal )
|
||||
{
|
||||
z_dist_to_goal = abs(self.origin[2]-enemy_goal.origin[2]);
|
||||
dist_to_goal_2d = Distance2D(self.origin,enemy_goal.origin);
|
||||
|
||||
if ( z_dist_to_goal < 10 || (reached_jump_apex && dist_to_goal_2d > 200) )
|
||||
{
|
||||
self BotPressButton("sprint");
|
||||
has_dodged_toward_goal = true;
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
time_waited += 0.05;
|
||||
if ( !self has_ball() || (time_waited > 0.75 && self IsOnGround()) )
|
||||
time_waited = 5.0;
|
||||
}
|
||||
self BotLookAtPoint( undefined );
|
||||
}
|
||||
self BotClearScriptGoal();
|
||||
}
|
||||
else if ( self.role == "attacker" )
|
||||
{
|
||||
free_balls = bot_ball_get_free_balls();
|
||||
if ( free_balls.size <= 0 )
|
||||
{
|
||||
// No free balls, so lets see if we need to kill or protect a ball carrier
|
||||
|
||||
if ( enemy_balls_carried.size > 0 )
|
||||
{
|
||||
closest_enemy_ball = self bot_ball_get_closest_ball(enemy_balls_carried);
|
||||
closest_enemy_ball_loc = closest_enemy_ball bot_ball_get_origin() - (0,0,75);
|
||||
if ( can_predict_carrier_loc )
|
||||
{
|
||||
if ( GetTime() > next_predict_carrier_location_time )
|
||||
{
|
||||
next_predict_carrier_location_time = GetTime() + 5000;
|
||||
|
||||
predicted_loc = undefined;
|
||||
path_to_goal = GetNodesOnPath( closest_enemy_ball_loc, my_goal.nearest_node.origin );
|
||||
if ( IsDefined(path_to_goal) && path_to_goal.size > 0 )
|
||||
predicted_loc = path_to_goal[Int(path_to_goal.size * RandomFloatRange(0.25,0.75))].origin;
|
||||
|
||||
self clear_defend_or_goal_if_necessary();
|
||||
if ( IsDefined(predicted_loc) && self find_ambush_node( predicted_loc, 512 ) )
|
||||
self BotSetScriptGoalNode( self.node_ambushing_from, "guard", self.ambush_yaw );
|
||||
else
|
||||
self BotSetScriptGoal( closest_enemy_ball_loc, 16, "guard" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self clear_defend_or_goal_if_necessary();
|
||||
self BotSetScriptGoal( closest_enemy_ball_loc, 16, "guard" );
|
||||
}
|
||||
}
|
||||
else if ( ally_balls_carried.size > 0 )
|
||||
{
|
||||
// One of my allies has the ball, so escort him
|
||||
|
||||
if ( !self bot_is_bodyguarding() )
|
||||
{
|
||||
closest_ally_ball = self bot_ball_get_closest_ball(ally_balls_carried);
|
||||
|
||||
self clear_defend_or_goal_if_necessary();
|
||||
self bot_guard_player(closest_ally_ball.carrier, 500);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No free balls, and no enemies carrying any balls. Means the ball is probably resetting, so just go to the closest ball start and wait for it there
|
||||
|
||||
ball_starts_sorted = get_array_of_closest( self.origin, level.ball_starts );
|
||||
self clear_defend_or_goal_if_necessary();
|
||||
self BotSetScriptGoal( ball_starts_sorted[0].origin, 16, "guard" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are still some free balls, so seek the closest one
|
||||
|
||||
ball_chosen = self bot_ball_get_closest_ball(free_balls);
|
||||
Assert(IsDefined(ball_chosen));
|
||||
|
||||
self clear_defend_or_goal_if_necessary( "objective" );
|
||||
if ( ball_chosen.ball_at_rest )
|
||||
{
|
||||
ball_origin = ball_chosen bot_ball_get_origin();
|
||||
if ( !self BotHasScriptGoal() || !bot_vectors_are_equal( ball_origin, self BotGetScriptGoal() ) )
|
||||
self BotSetScriptGoal( ball_origin, 16, "objective", undefined, SCR_CONST_BOT_BALL_OBJ_RADIUS );
|
||||
}
|
||||
else
|
||||
{
|
||||
self BotSetScriptGoal( ball_chosen.nearest_node.origin, 16, "objective", undefined, SCR_CONST_BOT_BALL_OBJ_RADIUS );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( self.role == "defender" );
|
||||
|
||||
ball_to_get_rid_of = undefined;
|
||||
free_balls = bot_ball_get_free_balls();
|
||||
foreach( ball in free_balls )
|
||||
{
|
||||
ball_dist_to_my_goal_sq = Distance2DSquared( ball bot_ball_get_origin(), my_goal.origin );
|
||||
if ( ball_dist_to_my_goal_sq < squared(get_ball_goal_protect_radius()) )
|
||||
{
|
||||
ball_to_get_rid_of = ball;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined(ball_to_get_rid_of) )
|
||||
{
|
||||
self clear_defend_or_goal_if_necessary();
|
||||
if ( ball_to_get_rid_of.ball_at_rest )
|
||||
self BotSetScriptGoal( ball_to_get_rid_of bot_ball_get_origin(), 16, "guard" );
|
||||
else
|
||||
self BotSetScriptGoal( ball_to_get_rid_of.nearest_node.origin, 16, "guard" );
|
||||
self bot_waittill_goal_or_fail(1.0);
|
||||
}
|
||||
else if ( !self bot_is_protecting() )
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
optional_params["score_flags"] = "strict_los";
|
||||
optional_params["override_origin_node"] = my_goal.nearest_node;
|
||||
self bot_protect_point( my_goal.nearest_node.origin, get_ball_goal_protect_radius(), optional_params );
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
watch_ball_pickup_and_loss()
|
||||
{
|
||||
had_ball = false;
|
||||
while(1)
|
||||
{
|
||||
if ( self has_ball() && !had_ball )
|
||||
{
|
||||
self childthread monitor_pass_throw();
|
||||
had_ball = true;
|
||||
self BotSetFlag("melee_critical_path",true);
|
||||
}
|
||||
else if ( !self has_ball() && had_ball )
|
||||
{
|
||||
self notify("bot_no_longer_has_ball");
|
||||
had_ball = false;
|
||||
self BotSetFlag("melee_critical_path",false);
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
monitor_pass_throw()
|
||||
{
|
||||
self endon("bot_no_longer_has_ball");
|
||||
|
||||
my_goal = level.ball_goals[self.team];
|
||||
enemy_goal = level.ball_goals[get_enemy_team(self.team)];
|
||||
|
||||
while(1)
|
||||
{
|
||||
if ( self.ball_can_pass_ally )
|
||||
{
|
||||
if ( IsDefined(self.pass_target) )
|
||||
{
|
||||
can_pass_ally = true;
|
||||
/#
|
||||
if ( bot_gametype_ignore_human_player_roles() && !IsAI(self.pass_target) )
|
||||
can_pass_ally = false;
|
||||
#/
|
||||
if ( can_pass_ally )
|
||||
{
|
||||
my_dist_to_goal_sq = DistanceSquared( self.origin, enemy_goal.origin );
|
||||
ally_dist_to_goal_sq = DistanceSquared( self.pass_target.origin, enemy_goal.origin );
|
||||
|
||||
if ( ally_dist_to_goal_sq <= my_dist_to_goal_sq )
|
||||
{
|
||||
bot_dir = AnglesToForward( self GetPlayerAngles() );
|
||||
bot_to_ally = VectorNormalize(self.pass_target.origin - self.origin);
|
||||
dot = VectorDot( bot_dir, bot_to_ally );
|
||||
if ( dot > 0.70 )
|
||||
{
|
||||
self BotLookAtPoint(self.pass_target.origin + (0,0,40),1.25,"script_forced");
|
||||
wait(0.25);
|
||||
self BotPressButton("throw");
|
||||
wait(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( self.ball_can_pass_enemy )
|
||||
{
|
||||
if ( IsDefined(self.enemy) && IsAlive(self.enemy) && self BotCanSeeEntity(self.enemy) )
|
||||
{
|
||||
can_throw_enemy = true;
|
||||
/#
|
||||
if ( bot_gametype_ignore_human_player_roles() && !IsAI(self.enemy) )
|
||||
can_throw_enemy = false;
|
||||
#/
|
||||
if ( can_throw_enemy )
|
||||
{
|
||||
my_dist_to_my_goal_sq = DistanceSquared( self.origin, my_goal.origin );
|
||||
too_close_to_goal = my_dist_to_my_goal_sq < squared(get_ball_goal_protect_radius());
|
||||
if ( !too_close_to_goal && DistanceSquared( self.origin, self.enemy.origin ) < squared(SCR_CONST_BALL_MAX_ENEMY_THROW_DIST) )
|
||||
{
|
||||
enemy_dir = AnglesToForward( self.enemy GetPlayerAngles() );
|
||||
enemy_to_bot = VectorNormalize(self.origin - self.enemy.origin);
|
||||
dot = VectorDot( enemy_dir, enemy_to_bot );
|
||||
if ( dot > 0.50 )
|
||||
{
|
||||
bot_dir = AnglesToForward( self GetPlayerAngles() );
|
||||
bot_to_enemy = -1 * enemy_to_bot;
|
||||
dot = VectorDot( bot_dir, bot_to_enemy );
|
||||
if ( dot > 0.77 )
|
||||
{
|
||||
self BotLookAtPoint(self.enemy.origin + (0,0,40),1.25,"script_forced");
|
||||
wait(0.25);
|
||||
self BotPressButton("attack");
|
||||
wait(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( self.ball_can_throw )
|
||||
{
|
||||
if ( self.health < 100 && self bot_ball_origin_can_see_goal( self.origin, enemy_goal ) )
|
||||
{
|
||||
self BotLookAtPoint( enemy_goal.origin, 1.25, "script_forced" );
|
||||
wait(0.25);
|
||||
self BotPressButton("attack");
|
||||
wait(1.0);
|
||||
}
|
||||
else if ( self.role == "defender" )
|
||||
{
|
||||
my_dist_to_my_goal_sq = DistanceSquared( self.origin, my_goal.origin );
|
||||
if ( my_dist_to_my_goal_sq < squared(get_ball_goal_protect_radius()) )
|
||||
{
|
||||
ball_throw_dir = AnglesToForward( (self GetPlayerAngles() * (0,1,1)) + (-30,0,0) );
|
||||
self BotLookAtPoint( self GetEye() + ball_throw_dir * 200, 1.25, "script_forced" );
|
||||
wait(0.25);
|
||||
self BotPressButton("attack");
|
||||
wait(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
ball_carrier_is_almost_visible( ball_carrier )
|
||||
{
|
||||
Assert( ball_carrier has_ball() );
|
||||
|
||||
nearest_node_self = self GetNearestNode();
|
||||
nearest_node_carrier = ball_carrier GetNearestNode();
|
||||
if ( IsDefined(nearest_node_self) && IsDefined(nearest_node_carrier) )
|
||||
{
|
||||
if ( NodesVisible(nearest_node_self, nearest_node_carrier, true) )
|
||||
return nearest_node_carrier;
|
||||
|
||||
linked_nodes = GetLinkedNodes(nearest_node_carrier);
|
||||
foreach( node in linked_nodes )
|
||||
{
|
||||
if ( NodesVisible(nearest_node_self, node, true) )
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
bot_ball_is_resetting()
|
||||
{
|
||||
return (self.compassIcons["friendly"] == "waypoint_ball_download") || (self.compassIcons["friendly"] == "waypoint_ball_upload");
|
||||
}
|
||||
|
||||
bot_ball_get_closest_ball( balls )
|
||||
{
|
||||
if ( balls.size == 1 )
|
||||
return balls[0];
|
||||
|
||||
closest_dist_sq = 99999999;
|
||||
closest_ball = undefined;
|
||||
|
||||
foreach ( ball in balls )
|
||||
{
|
||||
dist_to_ball_sq = DistanceSquared( self.origin, ball bot_ball_get_origin() );
|
||||
if ( dist_to_ball_sq < closest_dist_sq )
|
||||
{
|
||||
closest_dist_sq = dist_to_ball_sq;
|
||||
closest_ball = ball;
|
||||
}
|
||||
}
|
||||
|
||||
return closest_ball;
|
||||
}
|
||||
|
||||
bot_ball_get_origin() // self == ball
|
||||
{
|
||||
if ( IsDefined(self.carrier) )
|
||||
return self.curorigin;
|
||||
else
|
||||
return self.visuals[0].origin;
|
||||
}
|
||||
|
||||
clear_defend_or_goal_if_necessary( expected_new_goal_type )
|
||||
{
|
||||
if ( self bot_is_defending() )
|
||||
{
|
||||
self bot_defend_stop();
|
||||
}
|
||||
|
||||
if ( self BotGetScriptGoalType() == "objective" )
|
||||
{
|
||||
new_goal_is_objective = IsDefined(expected_new_goal_type) && expected_new_goal_type == "objective";
|
||||
if ( !new_goal_is_objective )
|
||||
self BotClearScriptGoal();
|
||||
}
|
||||
}
|
||||
|
||||
has_ball()
|
||||
{
|
||||
return IsDefined(self.ball_carried);
|
||||
}
|
||||
|
||||
bot_ball_get_free_balls()
|
||||
{
|
||||
free_balls = [];
|
||||
foreach(ball in level.balls)
|
||||
{
|
||||
if ( ball bot_ball_is_resetting() )
|
||||
continue;
|
||||
|
||||
if( !IsDefined(ball.carrier) )
|
||||
free_balls[free_balls.size] = ball;
|
||||
}
|
||||
|
||||
return free_balls;
|
||||
}
|
||||
|
||||
bot_ball_get_balls_carried_by_team(team)
|
||||
{
|
||||
team_balls = [];
|
||||
foreach(ball in level.balls)
|
||||
{
|
||||
if ( ball bot_ball_is_resetting() )
|
||||
continue;
|
||||
|
||||
if( IsDefined(ball.carrier) && ball.carrier.team == team )
|
||||
team_balls[team_balls.size] = ball;
|
||||
}
|
||||
|
||||
return team_balls;
|
||||
}
|
||||
|
||||
bot_ball_attacker_limit_for_team(team)
|
||||
{
|
||||
team_limit = bot_gametype_get_num_players_on_team(team);
|
||||
num_attackers_wanted_raw = team_limit * 0.67;
|
||||
|
||||
floor_num = floor(num_attackers_wanted_raw);
|
||||
ceil_num = ceil(num_attackers_wanted_raw);
|
||||
dist_to_floor = num_attackers_wanted_raw - floor_num;
|
||||
dist_to_ceil = ceil_num - num_attackers_wanted_raw;
|
||||
|
||||
if ( dist_to_floor < dist_to_ceil )
|
||||
num_attackers_wanted = int(floor_num);
|
||||
else
|
||||
num_attackers_wanted = int(ceil_num);
|
||||
|
||||
return num_attackers_wanted;
|
||||
}
|
||||
|
||||
bot_ball_defender_limit_for_team(team)
|
||||
{
|
||||
team_limit = bot_gametype_get_num_players_on_team(team);
|
||||
return ( team_limit - bot_ball_attacker_limit_for_team(team) );
|
||||
}
|
||||
|
||||
get_ball_goal_protect_radius()
|
||||
{
|
||||
if ( IsAlive(self) && !IsDefined(level.protect_radius) )
|
||||
{
|
||||
// Radius for a ball goal protect will be the minimum of 1000 or (average world size / 5)
|
||||
worldBounds = self BotGetWorldSize();
|
||||
average_side = (worldBounds[0] + worldBounds[1]) / 2;
|
||||
level.protect_radius = min(800,average_side/5.5);
|
||||
}
|
||||
|
||||
if ( !IsDefined(level.protect_radius) )
|
||||
return 900; // No bot has had a chance to calculate this yet, so just return a good default value
|
||||
|
||||
return level.protect_radius;
|
||||
}
|
||||
|
||||
get_allied_attackers_for_team(team)
|
||||
{
|
||||
return bot_gametype_get_allied_attackers_for_team(team, level.ball_goals[team].origin, get_ball_goal_protect_radius());
|
||||
}
|
||||
|
||||
get_allied_defenders_for_team(team)
|
||||
{
|
||||
return bot_gametype_get_allied_defenders_for_team(team, level.ball_goals[team].origin, get_ball_goal_protect_radius());
|
||||
}
|
531
raw/maps/mp/bots/_bots_gametype_common.gsc
Normal file
531
raw/maps/mp/bots/_bots_gametype_common.gsc
Normal file
@ -0,0 +1,531 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: bot_cache_entrances_to_bombzones()"
|
||||
"Summary: Caches entrance points using the level.bombZones array"
|
||||
"Example: bot_cache_entrances_to_bombzones();"
|
||||
///ScriptDocEnd
|
||||
============
|
||||
*/
|
||||
bot_cache_entrances_to_bombzones()
|
||||
{
|
||||
assert( IsDefined(level.bombZones) );
|
||||
|
||||
entrance_origin_points = [];
|
||||
entrance_labels = [];
|
||||
|
||||
index = 0;
|
||||
foreach( zone in level.bombZones )
|
||||
{
|
||||
entrance_origin_points[index] = Random(zone.botTargets).origin;
|
||||
entrance_labels[index] = "zone" + zone.label;
|
||||
index++;
|
||||
}
|
||||
|
||||
bot_cache_entrances( entrance_origin_points, entrance_labels );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: bot_cache_entrances_to_gametype_array()"
|
||||
"Summary: Caches entrance points to a gametype array, like flags or ball goals (or any array accessed with .origin and .script_label)"
|
||||
"MandatoryArg: <array> : An array of objects. They must have member variables .origin and .script_label"
|
||||
"MandatoryArg: <label_prefix> : Prefix to use for indices in the level.entrance_points array"
|
||||
"OptionalArg: <ignore_paths> : If true, will only calculate entrances and will not calculated paths between the entrances"
|
||||
"OptionalArg: <recalc_all> : If true, will clear out previous arrays and recalculate everything"
|
||||
"Example: bot_cache_entrances_to_gametype_array( level.flags, "flag" );"
|
||||
///ScriptDocEnd
|
||||
============
|
||||
*/
|
||||
bot_cache_entrances_to_gametype_array( array, label_prefix, ignore_paths, recalc_all )
|
||||
{
|
||||
assert( IsDefined(array) );
|
||||
|
||||
wait(1.0); // Wait for Path_AutoDisconnectPaths to run
|
||||
|
||||
entrance_origin_points = [];
|
||||
entrance_labels = [];
|
||||
int_array_index = 0;
|
||||
|
||||
foreach( i, element in array )
|
||||
{
|
||||
if ( IsDefined(array[i].botTarget) )
|
||||
{
|
||||
entrance_origin_points[int_array_index] = array[i].botTarget.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
array[i].nearest_node = GetClosestNodeInSight( array[i].origin );
|
||||
if ( !IsDefined( array[i].nearest_node ) )
|
||||
{
|
||||
nodes = GetNodesInRadiusSorted( array[i].origin, 256, 0 );
|
||||
if ( nodes.size > 0 )
|
||||
array[i].nearest_node = nodes[0];
|
||||
}
|
||||
|
||||
if ( !IsDefined( array[i].nearest_node ) )
|
||||
{
|
||||
AssertMsg("Could not calculate nearest node to flag origin " + array[i].origin);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( Distance(array[i].nearest_node.origin, array[i].origin) > 128 )
|
||||
{
|
||||
AssertMsg("Gametype object origin " + array[i].origin + " is too far away from the nearest pathnode, at origin " + array[i].nearest_node.origin);
|
||||
array[i].nearest_node = undefined;
|
||||
continue;
|
||||
}
|
||||
|
||||
entrance_origin_points[int_array_index] = array[i].nearest_node.origin;
|
||||
}
|
||||
entrance_labels[int_array_index] = label_prefix + array[i].script_label;
|
||||
int_array_index++;
|
||||
}
|
||||
|
||||
bot_cache_entrances( entrance_origin_points, entrance_labels, ignore_paths, recalc_all );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: bot_cache_entrances( <origin_array>, <label_array>, <ignore_paths> )"
|
||||
"Summary: Uses the origin and label array to fill out level.entrance_points"
|
||||
"MandatoryArg: <origin_array> : An array of origins (the points to find entrances for)"
|
||||
"MandatoryArg: <label_array> : An array of labels (to use for indices into the entrances array)"
|
||||
"OptionalArg: <ignore_paths> : If true, will only calculate entrances and will not calculated paths between the entrances"
|
||||
"OptionalArg: <recalc_all> : If true, will clear out previous arrays and recalculate everything"
|
||||
"Example: bot_cache_entrances( entrance_origin_points, entrance_labels );"
|
||||
///ScriptDocEnd
|
||||
============
|
||||
*/
|
||||
bot_cache_entrances( origin_array, label_array, ignore_paths, recalc_all )
|
||||
{
|
||||
assert( IsDefined(origin_array) );
|
||||
assert( IsDefined(label_array) );
|
||||
assert( origin_array.size > 0 );
|
||||
assert( label_array.size > 0 );
|
||||
assert( origin_array.size == label_array.size );
|
||||
|
||||
calculate_paths = !IsDefined(ignore_paths) || !ignore_paths;
|
||||
clear_out_old_data = IsDefined(recalc_all) && recalc_all;
|
||||
|
||||
wait(0.1);
|
||||
|
||||
if ( clear_out_old_data && calculate_paths )
|
||||
{
|
||||
nodes = GetAllNodes();
|
||||
foreach( node in nodes )
|
||||
node.on_path_from = undefined;
|
||||
}
|
||||
|
||||
entrance_points = [];
|
||||
|
||||
for ( i = 0; i < origin_array.size; i++ )
|
||||
{
|
||||
index = label_array[i];
|
||||
entrance_points[index] = FindEntrances( origin_array[i] );
|
||||
AssertEx( entrance_points[index].size > 0, "Entrance points for " + index + " at location " + origin_array[i] + " could not be calculated. Check pathgrid around that area" );
|
||||
wait(0.05);
|
||||
|
||||
for ( j = 0; j < entrance_points[index].size; j++ )
|
||||
{
|
||||
entrance = entrance_points[index][j];
|
||||
|
||||
// Mark entrance as precalculated (to save checks in other places)
|
||||
entrance.is_precalculated_entrance = true;
|
||||
|
||||
// Trace the entrance to determine prone visibility
|
||||
entrance.prone_visible_from[index] = entrance_visible_from( entrance.origin, origin_array[i], "prone" );
|
||||
wait(0.05);
|
||||
|
||||
// Trace the entrance to determine crouch visibility
|
||||
entrance.crouch_visible_from[index] = entrance_visible_from( entrance.origin, origin_array[i], "crouch" );
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
precalculated_paths = [];
|
||||
if ( calculate_paths )
|
||||
{
|
||||
for ( i = 0; i < origin_array.size; i++ )
|
||||
{
|
||||
for ( j = i+1; j < origin_array.size; j++ )
|
||||
{
|
||||
// Find path from origin_array[i] to origin_array[j]
|
||||
path = get_extended_path( origin_array[i], origin_array[j] );
|
||||
AssertEx( IsDefined(path), "Error calculating path from " + label_array[i] + " " + origin_array[i] + " to " + label_array[j] + " " + origin_array[j] + ". Check pathgrid around those areas" );
|
||||
|
||||
/#
|
||||
if ( !IsDefined(path) )
|
||||
continue; // avoid SRE spam when path is not defined
|
||||
#/
|
||||
|
||||
/#
|
||||
precalculated_paths[label_array[i]][label_array[j]] = path;
|
||||
precalculated_paths[label_array[j]][label_array[i]] = path;
|
||||
#/
|
||||
foreach( node in path )
|
||||
node.on_path_from[label_array[i]][label_array[j]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the arrays here, so we don't get bots trying to access a partially-defined array while we're still filling it out
|
||||
/#
|
||||
if ( calculate_paths )
|
||||
{
|
||||
if ( !IsDefined(level.precalculated_paths) )
|
||||
level.precalculated_paths = [];
|
||||
}
|
||||
#/
|
||||
|
||||
if ( !IsDefined(level.entrance_origin_points) )
|
||||
level.entrance_origin_points = [];
|
||||
|
||||
if ( !IsDefined(level.entrance_indices) )
|
||||
level.entrance_indices = [];
|
||||
|
||||
if ( !IsDefined(level.entrance_points) )
|
||||
level.entrance_points = [];
|
||||
|
||||
/#
|
||||
if ( calculate_paths )
|
||||
{
|
||||
if ( clear_out_old_data )
|
||||
level.precalculated_paths = precalculated_paths;
|
||||
else
|
||||
level.precalculated_paths = array_combine_non_integer_indices(level.precalculated_paths, precalculated_paths);
|
||||
}
|
||||
#/
|
||||
if ( clear_out_old_data )
|
||||
{
|
||||
level.entrance_origin_points = origin_array;
|
||||
level.entrance_indices = label_array;
|
||||
level.entrance_points = entrance_points;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.entrance_origin_points = array_combine(level.entrance_origin_points, origin_array);
|
||||
level.entrance_indices = array_combine(level.entrance_indices, label_array);
|
||||
level.entrance_points = array_combine_non_integer_indices(level.entrance_points, entrance_points);
|
||||
}
|
||||
|
||||
level.entrance_points_finished_caching = true; // This line should be the last line in the function
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
///ScriptDocBegin
|
||||
"Name: bot_add_missing_nodes( <struct_with_nodes>, trigger )"
|
||||
"Summary: Finds any nodes that might be near (underneath) the trigger but not technically in it, and adds them to the struct's .nodes array"
|
||||
"MandatoryArg: <struct_with_nodes> : A struct that contains a .nodes array (can be empty)"
|
||||
"MandatoryArg: <trigger> The trigger to test: "
|
||||
"Example: bot_add_missing_nodes(zone, zone.trig);"
|
||||
///ScriptDocEnd
|
||||
============
|
||||
*/
|
||||
bot_add_missing_nodes( struct_with_nodes, trigger )
|
||||
{
|
||||
Assert(IsDefined(struct_with_nodes.nodes));
|
||||
|
||||
if ( trigger.classname == "trigger_radius" )
|
||||
{
|
||||
test_nodes_in_radius = GetNodesInRadius( trigger.origin, trigger.radius, 0, 100 );
|
||||
nodes_in_radius_not_in_volume = array_remove_array(test_nodes_in_radius,struct_with_nodes.nodes);
|
||||
if ( nodes_in_radius_not_in_volume.size > 0 )
|
||||
struct_with_nodes.nodes = array_combine(struct_with_nodes.nodes,nodes_in_radius_not_in_volume);
|
||||
}
|
||||
else if ( trigger.classname == "trigger_multiple" || trigger.classname == "trigger_use_touch" )
|
||||
{
|
||||
// Find farthest bound
|
||||
bound_points[0] = trigger GetPointInBounds( 1, 1, 1 );
|
||||
bound_points[1] = trigger GetPointInBounds( 1, 1, -1 );
|
||||
bound_points[2] = trigger GetPointInBounds( 1, -1, 1 );
|
||||
bound_points[3] = trigger GetPointInBounds( 1, -1, -1 );
|
||||
bound_points[4] = trigger GetPointInBounds( -1, 1, 1 );
|
||||
bound_points[5] = trigger GetPointInBounds( -1, 1, -1 );
|
||||
bound_points[6] = trigger GetPointInBounds( -1, -1, 1 );
|
||||
bound_points[7] = trigger GetPointInBounds( -1, -1, -1 );
|
||||
|
||||
farthest_dist = 0;
|
||||
foreach( point in bound_points )
|
||||
{
|
||||
dist = Distance(point,trigger.origin);
|
||||
if ( dist > farthest_dist )
|
||||
farthest_dist = dist;
|
||||
}
|
||||
|
||||
test_nodes_in_radius = GetNodesInRadius( trigger.origin, farthest_dist, 0, 100 );
|
||||
foreach( node in test_nodes_in_radius )
|
||||
{
|
||||
if ( !IsPointInVolume( node.origin, trigger ) )
|
||||
{
|
||||
if ( IsPointInVolume( node.origin + (0,0,40), trigger ) || IsPointInVolume( node.origin + (0,0,80), trigger ) || IsPointInVolume( node.origin + (0,0,120), trigger ) )
|
||||
{
|
||||
struct_with_nodes.nodes = array_add(struct_with_nodes.nodes, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bot_setup_bombzone_bottargets()
|
||||
{
|
||||
wait(1.0); // Wait for Path_AutoDisconnectPaths to run
|
||||
|
||||
bot_setup_bot_targets(level.bombZones);
|
||||
level.bot_set_bombzone_bottargets = true;
|
||||
}
|
||||
|
||||
bot_setup_bot_targets(array)
|
||||
{
|
||||
foreach ( element in array )
|
||||
{
|
||||
if ( !IsDefined( element.botTargets ) )
|
||||
{
|
||||
element.botTargets = [];
|
||||
nodes_in_trigger = GetNodesInTrigger( element.trigger );
|
||||
foreach( node in nodes_in_trigger )
|
||||
{
|
||||
if ( !node NodeIsDisconnected() )
|
||||
element.botTargets = array_add(element.botTargets, node);
|
||||
}
|
||||
|
||||
element.nodes = element.botTargets;
|
||||
bot_add_missing_nodes(element,element.trigger);
|
||||
element.botTargets = element.nodes;
|
||||
element.nodes = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/#
|
||||
bot_gametype_ignore_human_player_roles()
|
||||
{
|
||||
return ( IsDefined(level.bot_gametype_ignore_human_player_roles) && level.bot_gametype_ignore_human_player_roles );
|
||||
}
|
||||
#/
|
||||
|
||||
bot_gametype_get_num_players_on_team(team)
|
||||
{
|
||||
num_on_team = 0;
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if ( IsTeamParticipant(player) && IsDefined(player.team) && player.team == team )
|
||||
{
|
||||
/#
|
||||
if ( bot_gametype_ignore_human_player_roles() && !IsAI(player) )
|
||||
continue;
|
||||
#/
|
||||
num_on_team++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_on_team;
|
||||
}
|
||||
|
||||
bot_gametype_get_allied_attackers_for_team(team, defend_origin, defend_radius)
|
||||
{
|
||||
attackers = bot_gametype_get_players_by_role( "attacker", team );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !IsAI(player) && IsDefined(player.team) && player.team == team )
|
||||
{
|
||||
/#
|
||||
if ( bot_gametype_ignore_human_player_roles() )
|
||||
continue;
|
||||
#/
|
||||
if ( DistanceSquared(defend_origin,player.origin) > squared(defend_radius) )
|
||||
attackers = array_add(attackers, player);
|
||||
}
|
||||
}
|
||||
|
||||
return attackers;
|
||||
}
|
||||
|
||||
bot_gametype_get_allied_defenders_for_team(team, defend_origin, defend_radius)
|
||||
{
|
||||
defenders = bot_gametype_get_players_by_role( "defender", team );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !IsAI(player) && IsDefined(player.team) && player.team == team )
|
||||
{
|
||||
/#
|
||||
if ( bot_gametype_ignore_human_player_roles() )
|
||||
continue;
|
||||
#/
|
||||
if ( DistanceSquared(defend_origin,player.origin) <= squared(defend_radius) )
|
||||
defenders = array_add(defenders, player);
|
||||
}
|
||||
}
|
||||
|
||||
return defenders;
|
||||
}
|
||||
|
||||
bot_gametype_set_role( new_role )
|
||||
{
|
||||
self.role = new_role;
|
||||
self BotClearScriptGoal();
|
||||
self bot_defend_stop();
|
||||
}
|
||||
|
||||
bot_gametype_get_players_by_role( role, team )
|
||||
{
|
||||
players = [];
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if ( IsDefined( player.team ) && IsAlive(player) && IsTeamParticipant(player) && player.team == team && IsDefined(player.role) && player.role == role )
|
||||
players[players.size] = player;
|
||||
}
|
||||
|
||||
return players;
|
||||
}
|
||||
|
||||
bot_gametype_initialize_attacker_defender_role()
|
||||
{
|
||||
attackers = [[level.bot_gametype_allied_attackers_for_team]](self.team);
|
||||
defenders = [[level.bot_gametype_allied_defenders_for_team]](self.team);
|
||||
attacker_limit = [[level.bot_gametype_attacker_limit_for_team]](self.team);
|
||||
defender_limit = [[level.bot_gametype_defender_limit_for_team]](self.team);
|
||||
|
||||
personality_type = level.bot_personality_type[self.personality];
|
||||
if ( personality_type == "active" )
|
||||
{
|
||||
if ( attackers.size >= attacker_limit )
|
||||
{
|
||||
// try to kick out a stationary bot
|
||||
kicked_out_bot = false;
|
||||
foreach ( attacker in attackers )
|
||||
{
|
||||
if ( IsAI(attacker) && level.bot_personality_type[attacker.personality] == "stationary" )
|
||||
{
|
||||
attacker.role = undefined;
|
||||
kicked_out_bot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( kicked_out_bot )
|
||||
self bot_gametype_set_role("attacker");
|
||||
else
|
||||
self bot_gametype_set_role("defender");
|
||||
}
|
||||
else
|
||||
{
|
||||
self bot_gametype_set_role("attacker");
|
||||
}
|
||||
}
|
||||
else if ( personality_type == "stationary" )
|
||||
{
|
||||
if ( defenders.size >= defender_limit )
|
||||
{
|
||||
// try to kick out an active bot
|
||||
kicked_out_bot = false;
|
||||
foreach ( defender in defenders )
|
||||
{
|
||||
if ( IsAI(defender) && level.bot_personality_type[defender.personality] == "active" )
|
||||
{
|
||||
defender.role = undefined;
|
||||
kicked_out_bot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( kicked_out_bot )
|
||||
self bot_gametype_set_role("defender");
|
||||
else
|
||||
self bot_gametype_set_role("attacker");
|
||||
}
|
||||
else
|
||||
{
|
||||
self bot_gametype_set_role("defender");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bot_gametype_attacker_defender_ai_director_update()
|
||||
{
|
||||
level notify("bot_gametype_attacker_defender_ai_director_update");
|
||||
level endon("bot_gametype_attacker_defender_ai_director_update");
|
||||
level endon("game_ended");
|
||||
|
||||
teams = ["allies","axis"];
|
||||
next_time_manage_roles = GetTime() + 2000;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if ( GetTime() > next_time_manage_roles )
|
||||
{
|
||||
next_time_manage_roles = GetTime() + 1000;
|
||||
foreach( team in teams )
|
||||
{
|
||||
attackers = [[level.bot_gametype_allied_attackers_for_team]](team);
|
||||
defenders = [[level.bot_gametype_allied_defenders_for_team]](team);
|
||||
attacker_limit = [[level.bot_gametype_attacker_limit_for_team]](team);
|
||||
defender_limit = [[level.bot_gametype_defender_limit_for_team]](team);
|
||||
|
||||
if ( attackers.size > attacker_limit )
|
||||
{
|
||||
ai_attackers = [];
|
||||
removed_attacker = false;
|
||||
foreach ( attacker in attackers )
|
||||
{
|
||||
if ( IsAI(attacker) )
|
||||
{
|
||||
if ( level.bot_personality_type[attacker.personality] == "stationary" )
|
||||
{
|
||||
attacker bot_gametype_set_role("defender");
|
||||
removed_attacker = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ai_attackers = array_add(ai_attackers, attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !removed_attacker && ai_attackers.size > 0 )
|
||||
Random(ai_attackers) bot_gametype_set_role("defender");
|
||||
}
|
||||
|
||||
if ( defenders.size > defender_limit )
|
||||
{
|
||||
ai_defenders = [];
|
||||
removed_defender = false;
|
||||
foreach ( defender in defenders )
|
||||
{
|
||||
if ( IsAI(defender) )
|
||||
{
|
||||
if ( level.bot_personality_type[defender.personality] == "active" )
|
||||
{
|
||||
defender bot_gametype_set_role("attacker");
|
||||
removed_defender = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ai_defenders = array_add(ai_defenders, defender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !removed_defender && ai_defenders.size > 0 )
|
||||
Random(ai_defenders) bot_gametype_set_role("attacker");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
557
raw/maps/mp/bots/_bots_gametype_conf.gsc
Normal file
557
raw/maps/mp/bots/_bots_gametype_conf.gsc
Normal file
@ -0,0 +1,557 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// This is called directly from native code on game startup after the _bots::main() is executed
|
||||
setup_callbacks();
|
||||
setup_bot_conf();
|
||||
}
|
||||
|
||||
/#
|
||||
empty_function_to_force_script_dev_compile() {}
|
||||
#/
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.bot_funcs["gametype_think"] = ::bot_conf_think;
|
||||
}
|
||||
|
||||
setup_bot_conf()
|
||||
{
|
||||
// Needs to occur regardless of whether bots are enabled / in play, because it is used with the Squad Member
|
||||
level.bot_tag_obj_radius = 200;
|
||||
|
||||
// If a tag is this distance above the bot's eyes, he will try to jump for it
|
||||
level.bot_tag_allowable_jump_height = 38;
|
||||
if ( isAugmentedGameMode() )
|
||||
level.bot_tag_allowable_jump_height += 170;
|
||||
|
||||
/#
|
||||
thread bot_conf_debug();
|
||||
#/
|
||||
}
|
||||
|
||||
/#
|
||||
SCR_CONST_DEBUG_SHOW_ALL_TAGS_NAME = "bot_DrawDebugShowAllTagsSeen";
|
||||
bot_conf_debug()
|
||||
{
|
||||
bot_waittill_bots_enabled();
|
||||
|
||||
SetDevDvarIfUninitialized( SCR_CONST_DEBUG_SHOW_ALL_TAGS_NAME, "0" );
|
||||
SetDevDvarIfUninitialized( "bot_DrawDebugTagNearestNodes", "0" );
|
||||
while(1)
|
||||
{
|
||||
if ( GetDvarInt("bot_DrawDebugGametype") == 1 )
|
||||
{
|
||||
if ( GetDvar(SCR_CONST_DEBUG_SHOW_ALL_TAGS_NAME) == "0" )
|
||||
{
|
||||
foreach( tag in level.dogtags )
|
||||
{
|
||||
if ( tag maps\mp\gametypes\_gameobjects::canInteractWith("allies") || tag maps\mp\gametypes\_gameobjects::canInteractWith("axis") )
|
||||
{
|
||||
bot_draw_circle( tag.curorigin, level.bot_tag_obj_radius, (1,0,0), false, 16 );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach( tag in level.dogtags )
|
||||
{
|
||||
if ( tag maps\mp\gametypes\_gameobjects::canInteractWith("allies") || tag maps\mp\gametypes\_gameobjects::canInteractWith("axis") )
|
||||
{
|
||||
bot_draw_circle( tag.curorigin, 10, (0,1,0), true, 16 );
|
||||
}
|
||||
}
|
||||
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if ( !IsDefined( player.team ) )
|
||||
continue;
|
||||
|
||||
if ( IsAlive(player) && IsDefined(player.tags_seen) )
|
||||
{
|
||||
foreach( tag in player.tags_seen )
|
||||
{
|
||||
if ( tag.tag maps\mp\gametypes\_gameobjects::canInteractWith(player.team) )
|
||||
{
|
||||
// Red line means its an enemy tag, blue line means an ally tag
|
||||
lineColor = undefined;
|
||||
if ( player.team != tag.tag.victim.team )
|
||||
lineColor = (1,0,0);
|
||||
else
|
||||
lineColor = (0,0,1);
|
||||
line( tag.tag.curorigin, player.origin + (0,0,20), lineColor, 1.0, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( GetDvar("bot_DrawDebugTagNearestNodes") == "1" )
|
||||
{
|
||||
foreach( tag in level.dogtags )
|
||||
{
|
||||
if ( tag maps\mp\gametypes\_gameobjects::canInteractWith("allies") || tag maps\mp\gametypes\_gameobjects::canInteractWith("axis") )
|
||||
{
|
||||
if ( IsDefined(tag.nearest_node) )
|
||||
{
|
||||
bot_draw_cylinder(tag.nearest_node.origin, 10, 10, 0.05, undefined, (0,0,1), true, 4);
|
||||
line(tag.curorigin, tag.nearest_node.origin, (0,0,1), 1.0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
bot_conf_think()
|
||||
{
|
||||
self notify( "bot_conf_think" );
|
||||
self endon( "bot_conf_think" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
self.tags_seen = [];
|
||||
|
||||
self childthread bot_watch_new_tags();
|
||||
|
||||
if ( self.personality == "camper" )
|
||||
{
|
||||
self.conf_camper_camp_tags = false;
|
||||
if ( !IsDefined( self.conf_camping_tag ) )
|
||||
self.conf_camping_tag = false;
|
||||
}
|
||||
|
||||
while ( true )
|
||||
{
|
||||
has_curr_tag = IsDefined(self.tag_getting);
|
||||
|
||||
needs_to_sprint = false;
|
||||
if ( has_curr_tag && self BotHasScriptGoal() )
|
||||
{
|
||||
script_goal = self BotGetScriptGoal();
|
||||
if ( bot_vectors_are_equal( self.tag_getting.ground_pos, script_goal ) )
|
||||
{
|
||||
// Script goal is this tag
|
||||
if ( self BotPursuingScriptGoal() )
|
||||
{
|
||||
needs_to_sprint = true;
|
||||
}
|
||||
}
|
||||
else if ( self bot_has_tactical_goal( "kill_tag" ) && self.tag_getting maps\mp\gametypes\_gameobjects::canInteractWith(self.team) )
|
||||
{
|
||||
// The bot has a goal to grab a tag, but yet his current script goal is not at the tag he's supposed to get
|
||||
// this can happen if the bot is heading toward a tag, and the tag's owner dies in his sight. Now the tag has moved locations, but since he
|
||||
// died onscreen, the tag remains in the bot's visible list and so the bot doesn't know he has to switch destinations
|
||||
self.tag_getting = undefined;
|
||||
has_curr_tag = false;
|
||||
}
|
||||
}
|
||||
|
||||
self BotSetFlag( "force_sprint", needs_to_sprint );
|
||||
|
||||
self.tags_seen = self bot_remove_invalid_tags( self.tags_seen );
|
||||
best_tag = self bot_find_best_tag_from_array( self.tags_seen, true );
|
||||
|
||||
desired_tag_exists = IsDefined(best_tag);
|
||||
if ( (has_curr_tag && !desired_tag_exists) || (!has_curr_tag && desired_tag_exists) || (has_curr_tag && desired_tag_exists && self.tag_getting != best_tag) )
|
||||
{
|
||||
// We're either setting self.tag_getting, clearing it, or changing it from one tag to a new one
|
||||
self.tag_getting = best_tag;
|
||||
self BotClearScriptGoal();
|
||||
self notify("stop_camping_tag");
|
||||
self clear_camper_data();
|
||||
self bot_abort_tactical_goal( "kill_tag" );
|
||||
}
|
||||
|
||||
if ( IsDefined(self.tag_getting) )
|
||||
{
|
||||
self.conf_camping_tag = false;
|
||||
if ( self.personality == "camper" && self.conf_camper_camp_tags )
|
||||
{
|
||||
// Camp this tag instead of grabbing it
|
||||
self.conf_camping_tag = true;
|
||||
if ( self should_select_new_ambush_point() )
|
||||
{
|
||||
if ( find_ambush_node( self.tag_getting.ground_pos, 1000 ) )
|
||||
{
|
||||
self childthread bot_camp_tag( self.tag_getting, "camp" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.conf_camping_tag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !self.conf_camping_tag )
|
||||
{
|
||||
if ( !self bot_has_tactical_goal( "kill_tag" ) )
|
||||
{
|
||||
extra_params = SpawnStruct();
|
||||
extra_params.script_goal_type = "objective";
|
||||
extra_params.objective_radius = level.bot_tag_obj_radius;
|
||||
self bot_new_tactical_goal( "kill_tag", self.tag_getting.ground_pos, 25, extra_params );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
did_something_else = false;
|
||||
if ( IsDefined( self.additional_tactical_logic_func ) )
|
||||
{
|
||||
did_something_else = self [[ self.additional_tactical_logic_func ]]();
|
||||
}
|
||||
|
||||
if ( !IsDefined(self.tag_getting) )
|
||||
{
|
||||
if ( !did_something_else )
|
||||
{
|
||||
self [[ self.personality_update_function ]]();
|
||||
}
|
||||
}
|
||||
|
||||
if ( GetTime() > self.next_time_check_tags )
|
||||
{
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
new_visible_tags = self bot_find_visible_tags( true );
|
||||
self.tags_seen = bot_combine_tag_seen_arrays( new_visible_tags, self.tags_seen );
|
||||
}
|
||||
|
||||
/#
|
||||
if ( GetDvarInt("bot_DrawDebugGametype") == 1 && GetDvar(SCR_CONST_DEBUG_SHOW_ALL_TAGS_NAME) == "0" )
|
||||
{
|
||||
if ( IsDefined(self.tag_getting) && self.health > 0 )
|
||||
{
|
||||
// Red line means the bot is camping the tag. Otherwise the line colors don't mean anything, just slightly different shades to distinguish between teams
|
||||
color = (0.5,0,0.5);
|
||||
if ( self.team == "allies" )
|
||||
color = (1,0,1);
|
||||
if ( IsDefined(self.conf_camper_camp_tags) && self.conf_camper_camp_tags )
|
||||
color = (1,0,0);
|
||||
Line( self.origin + (0,0,40), self.tag_getting.curorigin + (0,0,10), color, 1.0, true, 1 );
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
bot_check_tag_above_head( tag )
|
||||
{
|
||||
if ( IsDefined(tag.on_path_grid) && tag.on_path_grid )
|
||||
{
|
||||
self_eye_pos = self.origin + (0,0,55);
|
||||
if ( Distance2DSquared(tag.curorigin, self_eye_pos) < 12 * 12 )
|
||||
{
|
||||
tag_height_over_bot_head = tag.curorigin[2] - self_eye_pos[2];
|
||||
if ( tag_height_over_bot_head > 0 )
|
||||
{
|
||||
if ( tag_height_over_bot_head < level.bot_tag_allowable_jump_height )
|
||||
{
|
||||
if ( !IsDefined(self.last_time_jumped_for_tag) )
|
||||
self.last_time_jumped_for_tag = 0;
|
||||
|
||||
if ( GetTime() - self.last_time_jumped_for_tag > 3000 )
|
||||
{
|
||||
self.last_time_jumped_for_tag = GetTime();
|
||||
self thread bot_jump_for_tag();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tag.on_path_grid = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bot_jump_for_tag()
|
||||
{
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
|
||||
self BotSetStance("stand");
|
||||
wait(1.0);
|
||||
self BotPressButton("jump");
|
||||
wait(0.5);
|
||||
if ( isAugmentedGameMode() )
|
||||
self BotPressButton("jump");
|
||||
wait(0.5);
|
||||
self BotSetStance("none");
|
||||
}
|
||||
|
||||
bot_watch_new_tags()
|
||||
{
|
||||
while( 1 )
|
||||
{
|
||||
level waittill( "new_tag_spawned", newTag );
|
||||
// When a new tag spawns, look for them right away
|
||||
self.next_time_check_tags = -1;
|
||||
// If I was involved in this tag spawning (as victim or attacker), I automatically know about it
|
||||
if ( IsDefined( newTag ) )
|
||||
{
|
||||
if ( (IsDefined( newTag.victim ) && newTag.victim == self) || (IsDefined( newTag.attacker ) && newTag.attacker == self) )
|
||||
{
|
||||
if ( !IsDefined(newTag.on_path_grid) && !IsDefined(newTag.calculations_in_progress) )
|
||||
{
|
||||
thread calculate_tag_on_path_grid( newTag );
|
||||
waittill_tag_calculated_on_path_grid( newTag );
|
||||
|
||||
if ( newTag.on_path_grid )
|
||||
{
|
||||
new_tag_struct = SpawnStruct();
|
||||
new_tag_struct.origin = newTag.curorigin;
|
||||
new_tag_struct.tag = newTag;
|
||||
new_tag_fake_array[0] = new_tag_struct;
|
||||
self.tags_seen = bot_combine_tag_seen_arrays( new_tag_fake_array, self.tags_seen );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bot_combine_tag_seen_arrays( new_tag_seen_array, old_tag_seen_array )
|
||||
{
|
||||
new_array = old_tag_seen_array;
|
||||
foreach ( new_tag in new_tag_seen_array )
|
||||
{
|
||||
tag_already_exists_in_old_array = false;
|
||||
foreach ( old_tag in old_tag_seen_array )
|
||||
{
|
||||
if ( (new_tag.tag == old_tag.tag) && bot_vectors_are_equal( new_tag.origin, old_tag.origin ) )
|
||||
{
|
||||
tag_already_exists_in_old_array = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !tag_already_exists_in_old_array )
|
||||
new_array = array_add( new_array, new_tag );
|
||||
}
|
||||
|
||||
return new_array;
|
||||
}
|
||||
|
||||
bot_is_tag_visible( tag, nearest_node_self, fov_self )
|
||||
{
|
||||
if ( !tag.calculated_nearest_node )
|
||||
{
|
||||
tag.nearest_node = GetClosestNodeInSight(tag.curorigin);
|
||||
tag.calculated_nearest_node = true;
|
||||
}
|
||||
|
||||
if ( IsDefined(tag.calculations_in_progress) )
|
||||
return false; // ignore this tag while another bot is calculating for it
|
||||
|
||||
nearest_node_to_tag = tag.nearest_node;
|
||||
tag_first_time_ever_seen = !IsDefined(tag.on_path_grid);
|
||||
if ( IsDefined( nearest_node_to_tag ) && (tag_first_time_ever_seen || tag.on_path_grid) )
|
||||
{
|
||||
node_visible = (nearest_node_to_tag == nearest_node_self) || NodesVisible( nearest_node_to_tag, nearest_node_self, true );
|
||||
if ( node_visible )
|
||||
{
|
||||
node_within_fov = within_fov( self.origin, self.angles, tag.curorigin, fov_self );
|
||||
if ( node_within_fov )
|
||||
{
|
||||
if ( tag_first_time_ever_seen )
|
||||
{
|
||||
thread calculate_tag_on_path_grid( tag );
|
||||
waittill_tag_calculated_on_path_grid( tag );
|
||||
if ( !tag.on_path_grid )
|
||||
return false; // Subsequent checks will just return immediately at the top since this is now defined
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bot_find_visible_tags( require_los, optional_nearest_node_self, optional_fov_self )
|
||||
{
|
||||
nearest_node_self = undefined;
|
||||
if ( IsDefined(optional_nearest_node_self) )
|
||||
nearest_node_self = optional_nearest_node_self;
|
||||
else
|
||||
nearest_node_self = self GetNearestNode();
|
||||
|
||||
fov_self = undefined;
|
||||
if ( IsDefined(optional_fov_self) )
|
||||
fov_self = optional_fov_self;
|
||||
else
|
||||
fov_self = self BotGetFovDot();
|
||||
|
||||
visible_tags = [];
|
||||
|
||||
if ( IsDefined(nearest_node_self) )
|
||||
{
|
||||
foreach( tag in level.dogtags )
|
||||
{
|
||||
if ( tag maps\mp\gametypes\_gameobjects::canInteractWith(self.team) )
|
||||
{
|
||||
add_tag = false;
|
||||
if ( !require_los || (tag.attacker == self) ) // Can always see tags from your kills on the radar
|
||||
{
|
||||
if ( !IsDefined(tag.calculations_in_progress) ) // if the tag is still being calculated, then ignore it
|
||||
{
|
||||
if ( !IsDefined(tag.on_path_grid) )
|
||||
{
|
||||
level thread calculate_tag_on_path_grid( tag );
|
||||
waittill_tag_calculated_on_path_grid( tag );
|
||||
}
|
||||
|
||||
add_tag = (DistanceSquared(self.origin, tag.ground_pos) < 1000 * 1000) && tag.on_path_grid;
|
||||
}
|
||||
}
|
||||
else if ( bot_is_tag_visible( tag, nearest_node_self, fov_self ) )
|
||||
{
|
||||
add_tag = true;
|
||||
}
|
||||
|
||||
if ( add_tag )
|
||||
{
|
||||
new_tag_struct = SpawnStruct();
|
||||
new_tag_struct.origin = tag.curorigin;
|
||||
new_tag_struct.tag = tag;
|
||||
visible_tags = array_add( visible_tags, new_tag_struct );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visible_tags;
|
||||
}
|
||||
|
||||
calculate_tag_on_path_grid( tag )
|
||||
{
|
||||
tag endon("reset");
|
||||
|
||||
tag.calculations_in_progress = true;
|
||||
tag.on_path_grid = bot_point_is_on_pathgrid(tag.curorigin, undefined, level.bot_tag_allowable_jump_height + 55);
|
||||
if ( tag.on_path_grid )
|
||||
{
|
||||
tag.ground_pos = GetGroundPosition( tag.curorigin, 32 );
|
||||
if ( !IsDefined(tag.ground_pos) )
|
||||
tag.on_path_grid = false;
|
||||
}
|
||||
tag.calculations_in_progress = undefined;
|
||||
}
|
||||
|
||||
waittill_tag_calculated_on_path_grid(tag)
|
||||
{
|
||||
while( !IsDefined(tag.on_path_grid) )
|
||||
wait(0.05);
|
||||
}
|
||||
|
||||
bot_find_best_tag_from_array( tag_array, check_allies_getting_tag )
|
||||
{
|
||||
best_tag = undefined;
|
||||
if ( tag_array.size > 0 )
|
||||
{
|
||||
// find best tag
|
||||
best_tag_dist_sq = 99999 * 99999;
|
||||
foreach( tag_struct in tag_array )
|
||||
{
|
||||
num_allies_getting_tag = self get_num_allies_getting_tag( tag_struct.tag );
|
||||
if ( !check_allies_getting_tag || num_allies_getting_tag < 2 )
|
||||
{
|
||||
dist_self_to_tag_sq = DistanceSquared( tag_struct.tag.ground_pos, self.origin );
|
||||
if ( dist_self_to_tag_sq < best_tag_dist_sq )
|
||||
{
|
||||
best_tag = tag_struct.tag;
|
||||
best_tag_dist_sq = dist_self_to_tag_sq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_tag;
|
||||
}
|
||||
|
||||
bot_remove_invalid_tags( tags )
|
||||
{
|
||||
valid_tags = [];
|
||||
foreach ( tag_struct in tags )
|
||||
{
|
||||
// Need to check if the tag can still be interacted with and if it is in the same place as where we originally saw it
|
||||
// This is because the tags are reused in the game, so this is to check if the tag has already been picked up, or if the player whose tag it is
|
||||
// died again in a different spot, moving the tag to that location
|
||||
if ( tag_struct.tag maps\mp\gametypes\_gameobjects::canInteractWith(self.team) && bot_vectors_are_equal( tag_struct.tag.curorigin, tag_struct.origin ) )
|
||||
{
|
||||
if ( !self bot_check_tag_above_head( tag_struct.tag ) && tag_struct.tag.on_path_grid )
|
||||
valid_tags = array_add( valid_tags, tag_struct );
|
||||
}
|
||||
}
|
||||
|
||||
return valid_tags;
|
||||
}
|
||||
|
||||
get_num_allies_getting_tag( tag )
|
||||
{
|
||||
num = 0;
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if ( !IsDefined( player.team ) )
|
||||
continue;
|
||||
|
||||
if ( player.team == self.team && player != self )
|
||||
{
|
||||
if ( IsAI( player ) )
|
||||
{
|
||||
if ( IsDefined(player.tag_getting) && player.tag_getting == tag )
|
||||
num++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If player is within 400 distance from a tag, consider him to be going for it
|
||||
if ( DistanceSquared( player.origin, tag.curorigin ) < 400 * 400 )
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
bot_camp_tag( tag, goal_type, optional_endon )
|
||||
{
|
||||
self notify("bot_camp_tag");
|
||||
self endon("bot_camp_tag");
|
||||
self endon("stop_camping_tag");
|
||||
if ( IsDefined(optional_endon) )
|
||||
self endon(optional_endon);
|
||||
|
||||
self BotSetScriptGoalNode( self.node_ambushing_from, goal_type, self.ambush_yaw );
|
||||
result = self bot_waittill_goal_or_fail();
|
||||
|
||||
if ( result == "goal" )
|
||||
{
|
||||
nearest_node_to_tag = tag.nearest_node;
|
||||
if ( IsDefined( nearest_node_to_tag ) )
|
||||
{
|
||||
nodes_to_watch = FindEntrances( self.origin );
|
||||
nodes_to_watch = array_add( nodes_to_watch, nearest_node_to_tag );
|
||||
self childthread bot_watch_nodes( nodes_to_watch );
|
||||
}
|
||||
}
|
||||
}
|
344
raw/maps/mp/bots/_bots_gametype_ctf.gsc
Normal file
344
raw/maps/mp/bots/_bots_gametype_ctf.gsc
Normal file
@ -0,0 +1,344 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
/#
|
||||
SCR_CONST_CTF_BOTS_IGNORE_HUMAN_PLAYER_ROLES = false;
|
||||
#/
|
||||
|
||||
main()
|
||||
{
|
||||
// This is called directly from native code on game startup after the _bots::main() is executed
|
||||
setup_callbacks();
|
||||
setup_bot_ctf();
|
||||
}
|
||||
|
||||
/#
|
||||
empty_function_to_force_script_dev_compile() {}
|
||||
#/
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.bot_funcs["crate_can_use"] = ::crate_can_use;
|
||||
level.bot_funcs["gametype_think"] = ::bot_ctf_think;
|
||||
level.bot_funcs["get_watch_node_chance"] = ::bot_ctf_get_node_chance;
|
||||
}
|
||||
|
||||
setup_bot_ctf()
|
||||
{
|
||||
/#
|
||||
if ( SCR_CONST_CTF_BOTS_IGNORE_HUMAN_PLAYER_ROLES )
|
||||
level.bot_gametype_ignore_human_player_roles = true;
|
||||
#/
|
||||
|
||||
level.bot_gametype_attacker_limit_for_team = ::ctf_bot_attacker_limit_for_team;
|
||||
level.bot_gametype_defender_limit_for_team = ::ctf_bot_defender_limit_for_team;
|
||||
level.bot_gametype_allied_attackers_for_team = ::get_allied_attackers_for_team;
|
||||
level.bot_gametype_allied_defenders_for_team = ::get_allied_defenders_for_team;
|
||||
|
||||
bot_waittill_bots_enabled();
|
||||
|
||||
while( !IsDefined(level.teamFlags) )
|
||||
wait(0.05);
|
||||
|
||||
level.teamFlags["allies"].script_label = "allies";
|
||||
level.teamFlags["axis"].script_label = "axis";
|
||||
|
||||
bot_cache_entrances_to_gametype_array( level.teamFlags, "flag_" );
|
||||
|
||||
zone = GetZoneNearest( level.teamFlags["allies"].origin );
|
||||
if ( IsDefined( zone ) )
|
||||
BotZoneSetTeam( zone, "allies" );
|
||||
|
||||
zone = GetZoneNearest( level.teamFlags["axis"].origin );
|
||||
if ( IsDefined( zone ) )
|
||||
BotZoneSetTeam( zone, "axis" );
|
||||
|
||||
level.capZones["allies"].nearest_node = level.teamFlags["allies"].nearest_node;
|
||||
level.capZones["axis"].nearest_node = level.teamFlags["axis"].nearest_node;
|
||||
|
||||
thread bot_ctf_ai_director_update();
|
||||
|
||||
level.bot_gametype_precaching_done = true;
|
||||
}
|
||||
|
||||
crate_can_use( crate )
|
||||
{
|
||||
// Agents can only pickup boxes normally
|
||||
if ( IsAgent(self) && !IsDefined( crate.boxType ) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined(self.carryFlag) )
|
||||
return false;
|
||||
|
||||
return ( level.teamFlags[self.team] maps\mp\gametypes\_gameobjects::isHome() );
|
||||
}
|
||||
|
||||
bot_ctf_think()
|
||||
{
|
||||
self notify( "bot_ctf_think" );
|
||||
self endon( "bot_ctf_think" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( !IsDefined(level.bot_gametype_precaching_done) )
|
||||
wait(0.05);
|
||||
|
||||
self.next_time_hunt_carrier = 0;
|
||||
self.next_flag_hide_time = 0;
|
||||
self BotSetFlag("separation",0); // don't slow down when we get close to other bots
|
||||
self BotSetFlag("use_obj_path_style", true); // avoid lines of sight when pursuing objective goals
|
||||
|
||||
set_scripted_pathing_style = false;
|
||||
wants_scripted_pathing_style = false;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait(0.05);
|
||||
|
||||
if ( self.health <= 0 )
|
||||
continue;
|
||||
|
||||
if ( !IsDefined(self.role) )
|
||||
self bot_gametype_initialize_attacker_defender_role();
|
||||
|
||||
// Manage desired sprint
|
||||
sprint_desired = false;
|
||||
if ( self.role == "attacker" )
|
||||
{
|
||||
if ( IsDefined(self.carryFlag) )
|
||||
{
|
||||
// Sprint more frequently if we have the flag
|
||||
sprint_desired = true;
|
||||
}
|
||||
else if ( !IsDefined(level.flag_carriers[self.team]) )
|
||||
{
|
||||
// If no one yet has the flag, sprint more frequently when we get near it
|
||||
sprint_desired = DistanceSquared( self.origin, level.teamFlags[level.otherTeam[self.team]].curorigin ) < squared( get_flag_protect_radius());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !level.teamFlags[self.team] maps\mp\gametypes\_gameobjects::isHome() )
|
||||
{
|
||||
// Sprint more frequently if trying to pick up my flag from the ground
|
||||
sprint_desired = !IsDefined(level.flag_carriers[level.otherTeam[self.team]]);
|
||||
}
|
||||
}
|
||||
self BotSetFlag( "force_sprint", sprint_desired );
|
||||
|
||||
wants_scripted_pathing_style = false;
|
||||
if ( IsDefined(self.carryFlag) )
|
||||
{
|
||||
// I have the flag, so bring it home
|
||||
self clear_defend();
|
||||
if ( !IsDefined(level.flag_carriers[level.otherTeam[self.team]]) )
|
||||
{
|
||||
wants_scripted_pathing_style = true;
|
||||
|
||||
if ( !set_scripted_pathing_style )
|
||||
{
|
||||
set_scripted_pathing_style = true;
|
||||
self BotSetPathingStyle("scripted");
|
||||
}
|
||||
|
||||
self BotSetScriptGoal( level.capZones[self.team].curorigin, 16, "critical" );
|
||||
}
|
||||
else if ( GetTime() > self.next_flag_hide_time )
|
||||
{
|
||||
nodes = GetNodesInRadius( level.capZones[self.team].curorigin, 900, 0, 300 );
|
||||
hide_node = self BotNodePick( nodes, nodes.size * 0.15, "node_hide_anywhere" );
|
||||
if ( !IsDefined( hide_node ) )
|
||||
hide_node = level.capZones[self.team].nearest_node;
|
||||
|
||||
Assert(IsDefined(hide_node));
|
||||
success = self BotSetScriptGoalNode(hide_node, "critical");
|
||||
if ( success )
|
||||
self.next_flag_hide_time = GetTime() + 15000;
|
||||
}
|
||||
}
|
||||
else if ( self.role == "attacker" )
|
||||
{
|
||||
if ( IsDefined(level.flag_carriers[self.team]) )
|
||||
{
|
||||
// One of my friends has the flag, so escort him
|
||||
if ( !self bot_is_bodyguarding() )
|
||||
{
|
||||
self clear_defend();
|
||||
self BotClearScriptGoal();
|
||||
self bot_guard_player(level.flag_carriers[self.team], 500);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to get the flag
|
||||
self clear_defend();
|
||||
if ( self BotGetScriptGoalType() == "critical" )
|
||||
self BotClearScriptGoal();
|
||||
self BotSetScriptGoal( level.teamFlags[level.otherTeam[self.team]].curorigin, 16, "objective", undefined, 300 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( self.role == "defender" );
|
||||
|
||||
if ( !level.teamFlags[self.team] maps\mp\gametypes\_gameobjects::isHome() )
|
||||
{
|
||||
// My flag was taken, try to get it back
|
||||
if ( !IsDefined(level.flag_carriers[level.otherTeam[self.team]]) )
|
||||
{
|
||||
// no one is carrying flag, so run to it
|
||||
self clear_defend();
|
||||
self BotSetScriptGoal( level.teamFlags[self.team].curorigin, 16, "critical" );
|
||||
}
|
||||
else
|
||||
{
|
||||
flag_carrier = level.flag_carriers[level.otherTeam[self.team]];
|
||||
if ( GetTime() > self.next_time_hunt_carrier || self BotCanSeeEntity(flag_carrier) )
|
||||
{
|
||||
self clear_defend();
|
||||
self BotSetScriptGoal( flag_carrier.origin, 16, "critical" );
|
||||
self.next_time_hunt_carrier = GetTime() + RandomIntRange(4500,5500);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !self is_protecting_flag() )
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
optional_params["score_flags"] = "strict_los";
|
||||
optional_params["entrance_points_index"] = "flag_" + level.teamFlags[self.team].script_label;
|
||||
optional_params["nearest_node_to_center"] = level.teamFlags[self.team].nearest_node;
|
||||
self bot_protect_point( level.teamFlags[self.team].curorigin, get_flag_protect_radius(), optional_params );
|
||||
}
|
||||
}
|
||||
|
||||
if ( set_scripted_pathing_style && !wants_scripted_pathing_style )
|
||||
{
|
||||
set_scripted_pathing_style = false;
|
||||
self BotSetPathingStyle(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear_defend()
|
||||
{
|
||||
if ( self bot_is_defending() )
|
||||
{
|
||||
self bot_defend_stop();
|
||||
}
|
||||
}
|
||||
|
||||
is_protecting_flag()
|
||||
{
|
||||
return ( self bot_is_protecting() );
|
||||
}
|
||||
|
||||
get_flag_protect_radius()
|
||||
{
|
||||
if ( IsAlive(self) && !IsDefined(level.protect_radius) )
|
||||
{
|
||||
// Radius for a flag protect will be the minimum of 1000 or (average world size / 5)
|
||||
worldBounds = self BotGetWorldSize();
|
||||
average_side = (worldBounds[0] + worldBounds[1]) / 2;
|
||||
level.protect_radius = min(800,average_side/5.5);
|
||||
}
|
||||
|
||||
if ( !IsDefined(level.protect_radius) )
|
||||
return 900; // No bot has had a chance to calculate this yet, so just return a good default value
|
||||
|
||||
return level.protect_radius;
|
||||
}
|
||||
|
||||
ctf_bot_attacker_limit_for_team(team)
|
||||
{
|
||||
team_limit = bot_gametype_get_num_players_on_team(team);
|
||||
num_attackers_wanted_raw = team_limit * 0.67;
|
||||
|
||||
floor_num = floor(num_attackers_wanted_raw);
|
||||
ceil_num = ceil(num_attackers_wanted_raw);
|
||||
dist_to_floor = num_attackers_wanted_raw - floor_num;
|
||||
dist_to_ceil = ceil_num - num_attackers_wanted_raw;
|
||||
|
||||
if ( dist_to_floor < dist_to_ceil )
|
||||
num_attackers_wanted = int(floor_num);
|
||||
else
|
||||
num_attackers_wanted = int(ceil_num);
|
||||
|
||||
my_score = game["teamScores"][team];
|
||||
enemy_score = game["teamScores"][get_enemy_team(team)];
|
||||
|
||||
if ( my_score+1 < enemy_score )
|
||||
num_attackers_wanted = int(min(num_attackers_wanted+1,team_limit));
|
||||
|
||||
return num_attackers_wanted;
|
||||
}
|
||||
|
||||
ctf_bot_defender_limit_for_team(team)
|
||||
{
|
||||
team_limit = bot_gametype_get_num_players_on_team(team);
|
||||
return ( team_limit - ctf_bot_attacker_limit_for_team(team) );
|
||||
}
|
||||
|
||||
get_allied_attackers_for_team(team)
|
||||
{
|
||||
return bot_gametype_get_allied_attackers_for_team(team, level.capZones[team].curorigin, get_flag_protect_radius());
|
||||
}
|
||||
|
||||
get_allied_defenders_for_team(team)
|
||||
{
|
||||
return bot_gametype_get_allied_defenders_for_team(team, level.capZones[team].curorigin, get_flag_protect_radius());
|
||||
}
|
||||
|
||||
bot_ctf_ai_director_update()
|
||||
{
|
||||
level notify("bot_ctf_ai_director_update");
|
||||
level endon("bot_ctf_ai_director_update");
|
||||
level endon("game_ended");
|
||||
|
||||
level.flag_carriers = [];
|
||||
thread bot_gametype_attacker_defender_ai_director_update();
|
||||
|
||||
while(1)
|
||||
{
|
||||
level.flag_carriers["allies"] = undefined;
|
||||
level.flag_carriers["axis"] = undefined;
|
||||
foreach ( player in level.participants )
|
||||
{
|
||||
if ( IsAlive(player) && IsDefined(player.carryFlag) )
|
||||
{
|
||||
assert( IsTeamParticipant( player ) ); // only team participants should be carrying the flag, not squadmembers
|
||||
level.flag_carriers[player.team] = player;
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
bot_ctf_get_node_chance( node )
|
||||
{
|
||||
if ( node == self.node_closest_to_defend_center )
|
||||
{
|
||||
// Node closest to defend center always has a priority of 1.0
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if ( !self is_protecting_flag() )
|
||||
{
|
||||
// If we're not defending a flag, then we don't need to modify the priority
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
node_on_path_to_enemy_flag = node node_is_on_path_from_labels("flag_allies", "flag_axis");
|
||||
|
||||
if ( node_on_path_to_enemy_flag )
|
||||
return 1.0;
|
||||
|
||||
return 0.2;
|
||||
}
|
12
raw/maps/mp/bots/_bots_gametype_dm.gsc
Normal file
12
raw/maps/mp/bots/_bots_gametype_dm.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
1348
raw/maps/mp/bots/_bots_gametype_dom.gsc
Normal file
1348
raw/maps/mp/bots/_bots_gametype_dom.gsc
Normal file
File diff suppressed because it is too large
Load Diff
41
raw/maps/mp/bots/_bots_gametype_gun.gsc
Normal file
41
raw/maps/mp/bots/_bots_gametype_gun.gsc
Normal file
@ -0,0 +1,41 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// This is called directly from native code on game startup after the _bots::main() is executed
|
||||
setup_callbacks();
|
||||
setup_bot_gun();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.bot_funcs["gametype_think"] = ::bot_gun_think;
|
||||
}
|
||||
|
||||
setup_bot_gun()
|
||||
{
|
||||
}
|
||||
|
||||
bot_gun_think()
|
||||
{
|
||||
self notify( "bot_gun_think" );
|
||||
self endon( "bot_gun_think" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
// This game mode just performs normal personality logic
|
||||
while ( true )
|
||||
{
|
||||
self [[ self.personality_update_function ]]();
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user