#using scripts\codescripts\struct; #using scripts\shared\array_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\flag_shared; #using scripts\shared\hud_message_shared; #using scripts\shared\laststand_shared; #using scripts\shared\util_shared; #insert scripts\shared\shared.gsh; #insert scripts\shared\statstable_shared.gsh; #using scripts\shared\ai\zombie_utility; #using scripts\zm\_util; #using scripts\zm\_zm; #using scripts\zm\_zm_ai_faller; #using scripts\zm\_zm_audio; #using scripts\zm\_zm_bgb; #using scripts\zm\_zm_equipment; #using scripts\zm\_zm_laststand; #using scripts\zm\_zm_perks; #using scripts\zm\_zm_power; #using scripts\zm\_zm_powerups; #using scripts\zm\_zm_server_throttle; #using scripts\zm\_zm_stats; #using scripts\zm\_zm_spawner; #using scripts\zm\_zm_weapons; #insert scripts\zm\_zm_perks.gsh; #insert scripts\zm\_zm_utility.gsh; #namespace zm_utility; function init_utility() { // level thread edge_fog_start(); // level thread hudelem_count(); } function is_Classic() { return true; } function is_Standard() { dvar = GetDvarString( "ui_gametype" ); if ( dvar == "zstandard" ) { return true; } return false; } function ConvertSecondsToMilliseconds( seconds ) { return ( seconds * 1000 ); } function is_player() { return ( IsPlayer( self ) || ( IsDefined( self.pers ) && IS_TRUE( self.pers[ "isBot" ] ) ) ); } // self is Ai and chunk is selected piece function lerp( chunk ) { // anim_reach_aligned link = Spawn( "script_origin", self GetOrigin() ); // link is a new origin of the ai link.angles = self.first_node.angles ; // link now has the angles of itself, perhaps these angles aren't coming over.. self LinkTo(link); // link ai + origin to chunk + origin // link RotateTo(chunk.origin GetTagAngles("Tag_bottom"), level._CONTEXTUAL_GRAB_LERP_TIME); // this should angle him to spot // link MoveTo(chunk.origin GetTagOrigin("Tag_bottom"), level._CONTEXTUAL_GRAB_LERP_TIME); // this should move him over to spot // I need to have the offest for the bar link RotateTo(self.first_node.angles , level._CONTEXTUAL_GRAB_LERP_TIME); // this should angle him to spot link MoveTo(self.attacking_spot , level._CONTEXTUAL_GRAB_LERP_TIME); // this should move him over to spot //link RotateTo(self GetTagAngles ("Tag_player"), level._CONTEXTUAL_GRAB_LERP_TIME); // this should angle him to spot //link MoveTo(self GetTagOrigin ("Tag_player"), level._CONTEXTUAL_GRAB_LERP_TIME); // this should move him over to spot link util::waittill_multiple("rotatedone", "movedone"); self Unlink(); link Delete(); return; } function recalc_zombie_array() { /* enemies = []; level.current_zombie_array = []; enemies = GetAiSpeciesArray( level.zombie_team, "all" ); for( i = 0; i < enemies.size; i++ ) { if ( IS_TRUE( enemies[i].ignore_enemy_count ) ) { continue; } ARRAY_ADD( level.current_zombie_array, enemies[i] ); } level.current_zombie_count = level.current_zombie_array.size; */ } function init_zombie_run_cycle() { if ( isdefined( level.speed_change_round ) ) { if ( level.round_number >= level.speed_change_round ) { speed_percent = 0.2 + ( ( level.round_number - level.speed_change_round ) * 0.2 ); speed_percent = min( speed_percent, 1 ); change_round_max = Int( level.speed_change_max * speed_percent ); change_left = change_round_max - level.speed_change_num; if ( change_left == 0 ) { self zombie_utility::set_zombie_run_cycle(); return; } change_speed = RandomInt( 100 ); if ( change_speed > 80 ) { self change_zombie_run_cycle(); return; } zombie_count = zombie_utility::get_current_zombie_count(); zombie_left = level.zombie_ai_limit - zombie_count; if ( zombie_left == change_left ) // need to force the speed change at this point { self change_zombie_run_cycle(); return; } } } self zombie_utility::set_zombie_run_cycle(); } function change_zombie_run_cycle() { level.speed_change_num++; if ( level.gamedifficulty == 0 ) { self zombie_utility::set_zombie_run_cycle( "sprint" ); } else { /* iprintln( "walker round " + level.round_number + " change " + level.speed_change_num ); */ self zombie_utility::set_zombie_run_cycle( "walk" ); } self thread speed_change_watcher(); } function make_supersprinter() { self zombie_utility::set_zombie_run_cycle( "super_sprint" ); } function speed_change_watcher() { self waittill( "death" ); if ( level.speed_change_num > 0 ) { level.speed_change_num--; } } function move_zombie_spawn_location(spot) { self endon( "death" ); if(IsDefined(self.spawn_pos)) { self notify("risen", self.spawn_pos.script_string ); return; } self.spawn_pos = spot; // give target from spawn location to spawner to path if(IsDefined(spot.target)) { self.target = spot.target; } if(IsDefined(spot.zone_name)) { self.zone_name = spot.zone_name; self.previous_zone_name = spot.zone_name; } if(IsDefined(spot.script_parameters)) { self.script_parameters = spot.script_parameters; } if(!IsDefined(spot.script_noteworthy)) { spot.script_noteworthy = "spawn_location"; } tokens = StrTok( spot.script_noteworthy, " " ); foreach ( index, token in tokens ) { // time bomb needs to spawn zombies away from their original spawn point; this should point to a struct if ( IsDefined( self.spawn_point_override ) ) { spot = self.spawn_point_override; token = spot.script_noteworthy; } if ( token == "custom_spawner_entry" ) { next_token = index + 1; if ( IsDefined( tokens[ next_token ] ) ) { str_spawn_entry = tokens[ next_token ]; if ( IsDefined( level.custom_spawner_entry ) && IsDefined( level.custom_spawner_entry[ str_spawn_entry ] ) ) { self thread [[ level.custom_spawner_entry[ str_spawn_entry ] ]]( spot ); continue; } } } if(token == "riser_location") { self thread zm_spawner::do_zombie_rise(spot); } else if (token == "faller_location") { self thread zm_ai_faller::do_zombie_fall(spot); } else if (token == "spawn_location" ) { // anchor defined by some other custom spawner behavior, so bypass standard behavior if ( IsDefined( self.anchor ) ) { return; } self.anchor = spawn("script_origin", self.origin); self.anchor.angles = self.angles; self linkto(self.anchor); self.anchor thread anchor_delete_failsafe( self ); if( !isDefined( spot.angles ) ) { spot.angles = (0, 0, 0); } self Ghost(); self.anchor moveto(spot.origin, .05); self.anchor waittill("movedone"); // face goal target_org = zombie_utility::get_desired_origin(); if (IsDefined(target_org)) { anim_ang = VectorToAngles(target_org - self.origin); self.anchor RotateTo((0, anim_ang[1], 0), .05); self.anchor waittill("rotatedone"); } if(isDefined(level.zombie_spawn_fx)) { playfx(level.zombie_spawn_fx,spot.origin); } self unlink(); if(isDefined(self.anchor)) { self.anchor delete(); } if(!IS_TRUE(self.dontShow)) { self Show(); } self notify("risen", spot.script_string ); } } } // self is an anchor script_origin // There is a very small window where a zombie could be killed and the anchor would be left hanging around. function anchor_delete_failsafe( ai_zombie ) { ai_zombie endon( "risen" ); ai_zombie waittill( "death" ); if( isdefined( self ) ) { self Delete(); } } function run_spawn_functions() { self endon( "death" ); waittillframeend; // spawn functions should override default settings, so they need to be last for (i = 0; i < level.spawn_funcs[ self.team ].size; i++) { func = level.spawn_funcs[ self.team ][ i ]; util::single_thread(self, func[ "function" ], func[ "param1" ], func[ "param2" ], func[ "param3" ], func[ "param4" ], func[ "param5" ] ); } if ( IsDefined( self.spawn_funcs )) { for (i = 0; i < self.spawn_funcs.size; i++) { func = self.spawn_funcs[ i ]; util::single_thread(self, func[ "function" ], func[ "param1" ], func[ "param2" ], func[ "param3" ], func[ "param4" ] ); } /# self.saved_spawn_functions = self.spawn_funcs; #/ self.spawn_funcs = undefined; /# // keep them around in developer mode, for debugging self.spawn_funcs = self.saved_spawn_functions; self.saved_spawn_functions = undefined; #/ self.spawn_funcs = undefined; } } function create_simple_hud( client, team ) { if ( IsDefined( team ) ) { hud = NewTeamHudElem( team ); hud.team = team; } else { if( IsDefined( client ) ) { hud = NewClientHudElem( client ); } else { hud = NewHudElem(); } } level.hudelem_count++; hud.foreground = true; hud.sort = 1; hud.hidewheninmenu = false; return hud; } function destroy_hud() { level.hudelem_count--; self Destroy(); } // self = exterior_goal which is barrier_chunks function all_chunks_intact( barrier, barrier_chunks ) { if(isdefined(barrier.zbarrier)) { pieces = barrier.zbarrier GetZBarrierPieceIndicesInState("closed"); if(pieces.size != barrier.zbarrier GetNumZBarrierPieces()) { return false; } } else { for( i = 0; i < barrier_chunks.size; i++ ) // Count up barrier_chunks total size { if( barrier_chunks[i] get_chunk_state() != "repaired" ) // if any piece has the state of not repaired then return false { return false; } } } return true; // if the board has been repaired then return true } // self = exterior_goal which is barrier_chunks function no_valid_repairable_boards( barrier, barrier_chunks ) { if(isdefined(barrier.zbarrier)) { pieces = barrier.zbarrier GetZBarrierPieceIndicesInState("open"); if(pieces.size) { return false; } } else { for( i = 0; i < barrier_chunks.size; i++ ) // Count up barrier_chunks total size { if( barrier_chunks[i] get_chunk_state() == "destroyed" ) // if any piece has been destroyed return false { return false; } } } return true; // if any piece is not destroyed then return true } function is_Survival() { return false; } function is_Encounter() { return false; } function all_chunks_destroyed( barrier, barrier_chunks ) { if(isdefined(barrier.zbarrier)) { pieces = ArrayCombine(barrier.zbarrier GetZBarrierPieceIndicesInState("open"), barrier.zbarrier GetZBarrierPieceIndicesInState("opening"), true, false); if(pieces.size != barrier.zbarrier GetNumZBarrierPieces()) { return false; } } else if( IsDefined( barrier_chunks ) ) { ASSERT( IsDefined(barrier_chunks), "zombie_utility::all_chunks_destroyed - Barrier chunks undefined" ); for( i = 0; i < barrier_chunks.size; i++ ) { if( barrier_chunks[i] get_chunk_state() != "destroyed" ) { return false; } } } return true; } function check_point_in_playable_area( origin ) { playable_area = getentarray("player_volume","script_noteworthy"); if ( !IsDefined( level.check_model ) ) { level.check_model = spawn ("script_model", origin + (0,0,40)); } else { level.check_model.origin = origin + ( 0, 0, 40 ); } valid_point = false; for (i = 0; i < playable_area.size; i++) { if (level.check_model istouching(playable_area[i])) { valid_point = true; } } return valid_point; } //***************************************************************************** // origin - // zone_is_active - (optional) if set the zone must also be active as well // player_zones - (optional) optimization //***************************************************************************** //aka check_point_in_active_zone( origin ) function check_point_in_enabled_zone( origin, zone_is_active, player_zones ) { if( !IsDefined(player_zones) ) { player_zones = getentarray("player_volume","script_noteworthy"); } if( !isDefined( level.zones ) || !isDefined( player_zones ) ) { return true; } if ( !isdefined( level.e_check_point ) ) { // Just spawn the checker entity once and move it around as needed instead of creating and deleting all the time level.e_check_point = spawn( "script_origin", origin+(0, 0, 40) ); } else { level.e_check_point.origin = origin+(0,0,40); } one_valid_zone = false; for( i = 0; i < player_zones.size; i++ ) { if( level.e_check_point IsTouching( player_zones[i] ) ) { zone = level.zones[player_zones[i].targetname]; if( IsDefined(zone) && IS_TRUE(zone.is_enabled) ) { if( IsDefined(zone_is_active) && (zone_is_active == true) && !IS_TRUE(zone.is_active) ) { continue; } one_valid_zone = true; break; } } } return one_valid_zone; } function round_up_to_ten( score ) { new_score = score - score % 10; if( new_score < score ) { new_score += 10; } return new_score; } function round_up_score( score, value ) { score = int(score); // Make sure it's an int or modulus will die new_score = score - score % value; if( new_score < score ) { new_score += value; } return new_score; } function halve_score( n_score ) { n_score = n_score / 2; n_score = zm_utility::round_up_score( n_score, 10 ); return n_score; } function random_tan() { rand = randomint( 100 ); if(isDefined(level.char_percent_override) ) { percentNotCharred = level.char_percent_override; } else { percentNotCharred = 65; } // if( rand > percentNotCharred ) // { // self StartTanning(); // } // PI_CHANGE_END } // Returns the amount of places before the decimal, ie 1000 = 4, 100 = 3... function places_before_decimal( num ) { abs_num = abs( num ); count = 0; while( 1 ) { abs_num *= 0.1; // Really doing num / 10 count += 1; if( abs_num < 1 ) { return count; } } } function create_zombie_point_of_interest( attract_dist, num_attractors, added_poi_value, start_turned_on, initial_attract_func, arrival_attract_func,poi_team ) { if( !isDefined( added_poi_value ) ) { self.added_poi_value = 0; } else { self.added_poi_value = added_poi_value; } if( !isDefined( start_turned_on ) ) { start_turned_on = true; } if ( !isdefined( attract_dist ) ) { attract_dist = 1536; // Covers all available zombies within a given district } self.script_noteworthy = "zombie_poi"; self.poi_active = start_turned_on; if( isDefined( attract_dist ) ) { self.max_attractor_dist = attract_dist; self.poi_radius = attract_dist * attract_dist; } else // This poi has no maximum attract distance, it will attract all zombies { self.poi_radius = undefined; } self.num_poi_attracts = num_attractors; self.attract_to_origin = true; self.attractor_array = []; self.initial_attract_func = undefined; self.arrival_attract_func = undefined; if(isDefined(poi_team)) { self._team = poi_team; } // any special functions for initial reaction or arrival at the poi are stored here for get_zombie_point_of_intrest if( IsDefined( initial_attract_func ) ) { self.initial_attract_func = initial_attract_func; } if( IsDefined( arrival_attract_func ) ) { self.arrival_attract_func = arrival_attract_func; } ARRAY_ADD( level.zombie_poi_array, self ); self thread zm_utility::watch_for_poi_death(); } function watch_for_poi_death() { self waittill( "death" ); //Remove from list if ( IsInArray( level.zombie_poi_array, self ) ) { ArrayRemoveValue( level.zombie_poi_array, self ); } } function debug_draw_new_attractor_positions() { self endon( "death" ); while ( 1 ) { foreach( attract in self.attractor_positions ) { passed = BulletTracePassed( attract[0] + (0, 0, 24), self.origin + (0, 0, 24), false, self ); if ( passed ) { /# debugstar( attract[0], 6, (1,1,1) ); #/ } else { /# debugstar( attract[0], 6, (1,0,0) ); #/ } } WAIT_SERVER_FRAME; } } function create_zombie_point_of_interest_attractor_positions( num_attract_dists, attract_dist ) { self endon( "death" ); if( !isdefined( self.num_poi_attracts ) || (isDefined(self.script_noteworthy) && self.script_noteworthy != "zombie_poi" )) { return; } if( !isdefined( num_attract_dists ) ) // TODO Holdover from previous logic. Should be reevaluated { num_attract_dists = 4; } // Creates matrix of attraction points and assigns them to grenade queryResult = PositionQuery_Source_Navigation( self.origin, (attract_dist / 2), self.max_attractor_dist, (attract_dist / 2), (attract_dist / 2), true, (attract_dist / 2) ); if( queryResult.data.size < self.num_poi_attracts ) { self.num_poi_attracts = queryResult.data.size; } for ( i = 0; i < self.num_poi_attracts; i++ ) { if ( IS_TRUE( level.validate_poi_attractors ) ) { passed = BulletTracePassed( queryResult.data[i].origin + (0, 0, 24), self.origin + (0, 0, 24), false, self ); if ( passed ) { self.attractor_positions[i][0] = queryResult.data[i].origin; self.attractor_positions[i][1] = self; } } else { self.attractor_positions[i][0] = queryResult.data[i].origin; self.attractor_positions[i][1] = self; } } if( !IsDefined( self.attractor_positions ) ) { self.attractor_positions = []; } self.num_attract_dists = num_attract_dists; // The last index in the attractor_position arrays for each of the possible distances. The array is a holdover from previous logic. self.last_index = []; for( i = 0; i < num_attract_dists; i++ ) { self.last_index[i] = -1; } // Represents origin point TODO Holdover from previous logic. Should be reevaluated self.last_index[0] = 1; // Represents total number of attraction points TODO Holdover from previous logic. Should be reevaluated self.last_index[1] = self.attractor_positions.size; self.attract_to_origin = false; self notify( "attractor_positions_generated" ); level notify( "attractor_positions_generated" ); // self thread debug_draw_new_attractor_positions(); } function generated_radius_attract_positions( forward, offset, num_positions, attract_radius ) { self endon( "death" ); epsilon = 1; failed = 0; degs_per_pos = 360 / num_positions; for( i = offset; i < 360+offset; i += degs_per_pos ) { altforward = forward * attract_radius; rotated_forward = ( (cos(i)*altforward[0] - sin(i)*altforward[1]), (sin(i)*altforward[0] + cos(i)*altforward[1]), altforward[2] ); if(isDefined(level.poi_positioning_func)) { pos = [[level.poi_positioning_func]](self.origin, rotated_forward); } else if(IS_TRUE(level.use_alternate_poi_positioning)) { pos = zm_server_throttle::server_safe_ground_trace( "poi_trace", 10, self.origin + rotated_forward + ( 0, 0, 10 ) ); //only trace from 10 units above instead of 100 units } else { pos = zm_server_throttle::server_safe_ground_trace( "poi_trace", 10, self.origin + rotated_forward + ( 0, 0, 100 ) ); } if(!isDefined(pos)) { failed++; continue; } if(IS_TRUE(level.use_alternate_poi_positioning)) // Attractors are defined and sent to the array here { if ( isDefined( self ) && isDefined( self.origin ) ) { if( self.origin[2] >= (pos[2]-epsilon) && self.origin[2] - pos[2] <= 150 ) { pos_array = []; pos_array[0] = pos; pos_array[1] = self; ARRAY_ADD( self.attractor_positions , pos_array ); } } else { failed++; } } else if(abs( pos[2] - self.origin[2] ) < 60 ) { pos_array = []; pos_array[0] = pos; pos_array[1] = self; ARRAY_ADD( self.attractor_positions , pos_array ); } else { failed++; } } return failed; } function debug_draw_attractor_positions() { /# while( true ) { while( !isDefined( self.attractor_positions ) ) { WAIT_SERVER_FRAME; continue; } for( i = 0; i < self.attractor_positions.size; i++ ) { Line( self.origin, self.attractor_positions[i][0], (1, 0, 0), true, 1 ); } WAIT_SERVER_FRAME; if( !IsDefined( self ) ) { return; } } #/ } function debug_draw_claimed_attractor_positions() { /# while( true ) { while( !isDefined( self.claimed_attractor_positions ) ) { WAIT_SERVER_FRAME; continue; } for( i = 0; i < self.claimed_attractor_positions.size; i++ ) { Line( self.origin, self.claimed_attractor_positions[i][0], (0, 1, 0), true, 1 ); } WAIT_SERVER_FRAME; if( !IsDefined( self ) ) { return; } } #/ } function get_zombie_point_of_interest( origin, poi_array ) { AIProfile_BeginEntry( "get_zombie_point_of_interest" ); if ( IS_TRUE( self.ignore_all_poi ) ) { AIProfile_EndEntry(); return undefined; } curr_radius = undefined; if ( isdefined( poi_array ) ) { ent_array = poi_array; } else { ent_array = level.zombie_poi_array;//getEntArray( "zombie_poi", "script_noteworthy" ); } best_poi = undefined; position = undefined; best_dist = 10000 * 10000; for( i = 0; i < ent_array.size; i++ ) { if( !isDefined( ent_array[i] ) || !isDefined( ent_array[i].poi_active ) || !ent_array[i].poi_active ) { continue; } if ( isDefined( self.ignore_poi_targetname ) && self.ignore_poi_targetname.size > 0 ) { if ( isDefined( ent_array[i].targetname ) ) { ignore = false; for ( j = 0; j < self.ignore_poi_targetname.size; j++ ) { if ( ent_array[i].targetname == self.ignore_poi_targetname[j] ) { ignore = true; break; } } if ( ignore ) { continue; } } } if ( isDefined( self.ignore_poi ) && self.ignore_poi.size > 0 ) { ignore = false; for ( j = 0; j < self.ignore_poi.size; j++ ) { if ( self.ignore_poi[j] == ent_array[i] ) { ignore = true; break; } } if ( ignore ) { continue; } } dist = distanceSquared( origin, ent_array[i].origin ); dist -= ent_array[i].added_poi_value; if( isDefined( ent_array[i].poi_radius ) ) { curr_radius = ent_array[i].poi_radius; } if( (!isDefined( curr_radius ) || dist < curr_radius) && dist < best_dist && ent_array[i] can_attract(self) ) { best_poi = ent_array[i]; best_dist = dist; } } if( isDefined( best_poi ) ) { if(isDefined(best_poi._team)) { if(isDefined(self._race_team) && self._race_team != best_poi._team) { AIProfile_EndEntry(); return undefined; } } if( IS_TRUE( best_poi._new_ground_trace ) ) // needed for the bhb so it won't trace through metal clip { position = []; position[0] = groundpos_ignore_water_new( best_poi.origin + (0, 0, 100) ); position[1] = self; } else if( isDefined( best_poi.attract_to_origin ) && best_poi.attract_to_origin ) // Override, currently only used for monkeys in the air. { position = []; position[0] = groundpos( best_poi.origin + (0, 0, 100) ); position[1] = self; } else { position = self add_poi_attractor( best_poi ); } if( IsDefined( best_poi.initial_attract_func ) ) { self thread [[ best_poi.initial_attract_func ]]( best_poi ); } if( IsDefined( best_poi.arrival_attract_func ) ) { self thread [[ best_poi.arrival_attract_func ]]( best_poi ); } } AIProfile_EndEntry(); return position; } function activate_zombie_point_of_interest() { if( self.script_noteworthy != "zombie_poi" ) { return; } self.poi_active = true; } function deactivate_zombie_point_of_interest( dont_remove ) { if( self.script_noteworthy != "zombie_poi" ) { return; } self.attractor_array = array::remove_undefined( self.attractor_array ); for( i = 0; i < self.attractor_array.size; i++ ) { self.attractor_array[i] notify( "kill_poi" ); } self.attractor_array = []; self.claimed_attractor_positions = []; self.poi_active = false; if ( IS_TRUE( dont_remove ) ) { return; } //Remove from list if ( isdefined( self ) ) { if ( IsInArray( level.zombie_poi_array, self ) ) { ArrayRemoveValue( level.zombie_poi_array, self ); } } } //PI_CHANGE_BEGIN - 6/18/09 JV This works to help set "wait" points near the stage if all players are in the process teleportation. //It is unlike the previous function in that you dictate the poi. function assign_zombie_point_of_interest (origin, poi) { position = undefined; doremovalthread = false; if (IsDefined(poi) && poi can_attract(self)) { //don't want to touch add poi attractor, but yeah, this is kind of weird if (!IsDefined(poi.attractor_array) || ( IsDefined(poi.attractor_array) && !IsInArray( poi.attractor_array, self ) )) doremovalthread = true; position = self add_poi_attractor( poi ); //now that I know this is the first time they've been added, set up the thread to remove them from the array if (IsDefined(position) && doremovalthread && IsInArray( poi.attractor_array, self )) self thread update_on_poi_removal( poi ); } return position; } //PI_CHANGE_END function remove_poi_attractor( zombie_poi ) { if( !isDefined( zombie_poi ) || !isDefined( zombie_poi.attractor_array ) ) { return; } for( i = 0; i < zombie_poi.attractor_array.size; i++ ) { if( zombie_poi.attractor_array[i] == self ) { ArrayRemoveValue( zombie_poi.attractor_array, zombie_poi.attractor_array[i] ); ArrayRemoveValue( zombie_poi.claimed_attractor_positions, zombie_poi.claimed_attractor_positions[i] ); if ( isdefined( self ) ) { self notify( "kill_poi" ); } } } } function array_check_for_dupes_using_compare( array, single, is_equal_fn ) { for( i = 0; i < array.size; i++ ) { if( [[is_equal_fn]]( array[i], single ) ) { return false; } } return true; } function poi_locations_equal( loc1, loc2 ) { return loc1[0]==loc2[0]; } function add_poi_attractor( zombie_poi ) { if( !isDefined( zombie_poi ) ) { return; } if( !isDefined( zombie_poi.attractor_array ) ) { zombie_poi.attractor_array = []; } // If we are not yet an attractor to this poi, claim an attractor position and start attracting to it if( !IsInArray( zombie_poi.attractor_array, self ) ) { if( !isDefined( zombie_poi.claimed_attractor_positions ) ) { zombie_poi.claimed_attractor_positions = []; } if( !isDefined( zombie_poi.attractor_positions ) || zombie_poi.attractor_positions.size <= 0 ) { return undefined; } start = -1; end = -1; last_index = -1; for( i = 0; i < 4; i++ ) { if( zombie_poi.claimed_attractor_positions.size < zombie_poi.last_index[i] ) { start = last_index+1; end = zombie_poi.last_index[i]; break; } last_index = zombie_poi.last_index[i]; } best_dist = 10000*10000; best_pos = undefined; if( start < 0 ) { start = 0; } if( end < 0 ) { return undefined; } for( i = int(start); i <= int(end); i++ ) { if(!isDefined(zombie_poi.attractor_positions[i])) { continue; } //if( !IsInArray( zombie_poi.claimed_attractor_positions, zombie_poi.attractor_positions[i] ) ) if( array_check_for_dupes_using_compare( zombie_poi.claimed_attractor_positions, zombie_poi.attractor_positions[i], &poi_locations_equal ) ) { if ( isDefined( zombie_poi.attractor_positions[i][0] ) && isDefined( self.origin ) ) { dist = distancesquared( zombie_poi.attractor_positions[i][0], zombie_poi.origin ); if( dist < best_dist || !isDefined( best_pos ) ) { best_dist = dist; best_pos = zombie_poi.attractor_positions[i]; } } } } if( !isDefined( best_pos ) ) { if ( IS_TRUE( level.validate_poi_attractors ) ) { valid_pos = []; valid_pos[0] = zombie_poi.origin; valid_pos[1] = zombie_poi; return valid_pos; } return undefined; } ARRAY_ADD( zombie_poi.attractor_array, self ); self thread update_poi_on_death( zombie_poi ); ARRAY_ADD( zombie_poi.claimed_attractor_positions, best_pos ); return best_pos; } else { for( i = 0; i < zombie_poi.attractor_array.size; i++ ) { if( zombie_poi.attractor_array[i] == self ) { if( isDefined( zombie_poi.claimed_attractor_positions ) && isDefined( zombie_poi.claimed_attractor_positions[i] ) ) { return zombie_poi.claimed_attractor_positions[i]; } } } } return undefined; } function can_attract( attractor ) { if( !isDefined( self.attractor_array ) ) { self.attractor_array = []; } //Raven Begin - Allow only selected zombies to be attracted if( isDefined(self.attracted_array) && !IsInArray(self.attracted_array, attractor) ) { return false; } //Raven End if( IsInArray( self.attractor_array, attractor ) ) { return true; } if( isDefined(self.num_poi_attracts) && self.attractor_array.size >= self.num_poi_attracts ) { return false; } return true; } function update_poi_on_death( zombie_poi ) { self endon( "kill_poi" ); self waittill( "death" ); self remove_poi_attractor( zombie_poi ); } //PI_CHANGE_BEGIN - 6/18/09 JV This was set up to work with assign_zombie_point_of_interest (which works with the teleportation in theater). //The poi attractor array needs to be emptied when a player is teleported out of projection room (if they were all in there). //As a result, we wait for the poi's death (I'm sending that notify via the level script) function update_on_poi_removal (zombie_poi ) { zombie_poi waittill( "death" ); if( !isDefined( zombie_poi.attractor_array ) ) return; for( i = 0; i < zombie_poi.attractor_array.size; i++ ) { if( zombie_poi.attractor_array[i] == self ) { ArrayRemoveIndex( zombie_poi.attractor_array, i ); ArrayRemoveIndex( zombie_poi.claimed_attractor_positions, i ); } } } //PI_CHANGE_END function invalidate_attractor_pos( attractor_pos, zombie ) { if( !isDefined( self ) || !isDefined( attractor_pos ) ) { wait( 0.1 ); return undefined; } if( isDefined( self.attractor_positions) && !array_check_for_dupes_using_compare( self.attractor_positions, attractor_pos, &poi_locations_equal ) ) { index = 0; for( i = 0; i < self.attractor_positions.size; i++ ) { if( poi_locations_equal( self.attractor_positions[i], attractor_pos) ) { index = i; } } for( i = 0; i < self.last_index.size; i++ ) { if( index <= self.last_index[i] ) { self.last_index[i]--; } } ArrayRemoveValue( self.attractor_array, zombie ); ArrayRemoveValue( self.attractor_positions, attractor_pos ); for( i = 0; i < self.claimed_attractor_positions.size; i++ ) { if( self.claimed_attractor_positions[i][0] == attractor_pos[0] ) { ArrayRemoveValue( self.claimed_attractor_positions, self.claimed_attractor_positions[i] ); } } } else { wait( 0.1 ); } return get_zombie_point_of_interest( zombie.origin ); } function remove_poi_from_ignore_list( poi ) { if ( isDefined( self.ignore_poi ) && self.ignore_poi.size > 0 ) { for ( i = 0; i < self.ignore_poi.size; i++ ) { if ( self.ignore_poi[i] == poi ) { ArrayRemoveValue( self.ignore_poi, self.ignore_poi[i] ); return; } } } } function add_poi_to_ignore_list( poi ) { if ( !isDefined( self.ignore_poi ) ) { self.ignore_poi = []; } add_poi = true; if ( self.ignore_poi.size > 0 ) { for ( i = 0; i < self.ignore_poi.size; i++ ) { if ( self.ignore_poi[i] == poi ) { add_poi = false; break; } } } if ( add_poi ) { self.ignore_poi[self.ignore_poi.size] = poi; } } //------------------------------------------------------------------------------- // player should be within a couple feet of enemy //------------------------------------------------------------------------------- function default_validate_enemy_path_length( player ) { max_dist = 1296; d = DistanceSquared( self.origin, player.origin ); if ( d <= max_dist ) { return true; } return false; } function get_closest_valid_player( origin, ignore_player ) { AIProfile_BeginEntry( "get_closest_valid_player" ); valid_player_found = false; players = GetPlayers(); if( isdefined( level.get_closest_valid_player_override ) ) { players = [[ level.get_closest_valid_player_override ]](); } b_designated_target_exists = false; for(i = 0; i < players.size; i++ ) { player = players[i]; //if( !is_player_valid( players[i], true ) ) if( !player.am_i_valid ) { continue; } if( isDefined( level.evaluate_zone_path_override ) ) { if( ![[ level.evaluate_zone_path_override ]]( player ) ) { array::add( ignore_player, player ); } } // is any player a designated target? if( IS_TRUE( player.b_is_designated_target ) ) { b_designated_target_exists = true; } } if( IsDefined( ignore_player ) ) { for(i = 0; i < ignore_player.size; i++ ) { ArrayRemoveValue( players, ignore_player[i] ); } } // pre-cull any players that are in last stand or not designated target when a designated target is present done = false; while ( players.size && !done ) { done = true; for(i = 0; i < players.size; i++ ) { player = players[i]; //if( !is_player_valid( player, true ) ) if( !player.am_i_valid ) { ArrayRemoveValue( players, player ); done = false; break; } if( b_designated_target_exists && !IS_TRUE( player.b_is_designated_target ) ) { ArrayRemoveValue( players, player ); done = false; break; } } } if( players.size == 0 ) { AIProfile_EndEntry(); return undefined; } while( !valid_player_found ) { // find the closest player if( IsDefined( self.closest_player_override ) ) { player = [[ self.closest_player_override ]]( origin, players ); } else if( isdefined( level.closest_player_override ) ) { player = [[ level.closest_player_override ]]( origin, players ); } else { player = ArrayGetClosest( origin, players ); } if( !isdefined( player ) || players.size == 0 ) { AIProfile_EndEntry(); return undefined; } if( IS_TRUE(level.allow_zombie_to_target_ai) || IS_TRUE(player.allow_zombie_to_target_ai) ) { AIProfile_EndEntry(); return player; } // make sure they're not a zombie or in last stand //if( !is_player_valid( player, true ) ) if( !player.am_i_valid ) { // unlikely to get here unless there is a wait in one of the closest player overrides ArrayRemoveValue( players, player ); if( players.size == 0 ) { AIProfile_EndEntry(); return undefined; } continue; } AIProfile_EndEntry(); return player; } } //modified version of get_closest_valid_player that gives perfect info and sets up ignoreEnts to respect the ignore_player array. function update_valid_players( origin, ignore_player ) { AIProfile_BeginEntry( "update_valid_players" ); valid_player_found = false; players = ArrayCopy( level.players ); // Have to use ArrayCopy otherwise the ArrayRemove calls below will remove the players from level.players //new zombie enemy selection foreach( player in players ) { self SetIgnoreEnt( player, true ); } b_designated_target_exists = false; for(i = 0; i < players.size; i++ ) { player = players[i]; //if( !is_player_valid( players[i], true ) ) if( !player.am_i_valid ) { continue; } if( isDefined( level.evaluate_zone_path_override ) ) { if( ![[ level.evaluate_zone_path_override ]]( player ) ) { array::add( ignore_player, player ); } } // is any player a designated target? if( IS_TRUE( player.b_is_designated_target ) ) { b_designated_target_exists = true; } } if( IsDefined( ignore_player ) ) { for(i = 0; i < ignore_player.size; i++ ) { ArrayRemoveValue( players, ignore_player[i] ); } } // pre-cull any players that are in last stand or not designated target when a designated target is present done = false; while ( players.size && !done ) { done = true; for(i = 0; i < players.size; i++ ) { player = players[i]; //if( !is_player_valid( player, true ) ) if( !player.am_i_valid ) { ArrayRemoveValue( players, player ); done = false; break; } if( b_designated_target_exists && !IS_TRUE( player.b_is_designated_target ) ) { ArrayRemoveValue( players, player ); done = false; break; } } } //new zombie enemy selection foreach( player in players ) { self SetIgnoreEnt( player, false ); self GetPerfectInfo( player ); } AIProfile_EndEntry(); } function is_player_valid( player, checkIgnoreMeFlag,ignore_laststand_players ) { if( !IsDefined( player ) ) { return false; } if( !IsAlive( player ) ) { return false; } if( !IsPlayer( player ) ) { return false; } if( IsDefined(player.is_zombie) && player.is_zombie == true ) { return false; } if( player.sessionstate == "spectator" ) { return false; } if( player.sessionstate == "intermission" ) { return false; } if( IS_TRUE( level.intermission ) ) { return false; } if( !IS_TRUE( ignore_laststand_players ) ) { if( player laststand::player_is_in_laststand() ) { return false; } } //T6.5todo if ( player isnotarget() ) //T6.5todo { //T6.5todo return false; //T6.5todo } //We only want to check this from the zombie attack script if( IS_TRUE(checkIgnoreMeFlag) && player.ignoreme ) { //IPrintLnBold(" ignore me "); return false; } //for additional level specific checks if( IsDefined( level.is_player_valid_override ) ) { return [[ level.is_player_valid_override ]]( player ); } return true; } function get_number_of_valid_players() { players = GetPlayers(); num_player_valid = 0; for( i = 0 ; i < players.size; i++ ) { if( is_player_valid( players[i] ) ) num_player_valid += 1; } return num_player_valid; } function in_revive_trigger() { if (isdefined(self.rt_time) && self.rt_time + 100 >= GetTime()) return self.in_rt_cached; self.rt_time = GetTime(); players = level.players; //GetPlayers(); for( i = 0; i < players.size; i++ ) { current_player = players[i]; //if( !IsDefined( current_player )) || !IsAlive( current_player ) ) //{ // continue; //} if( IsDefined( current_player ) && IsDefined( current_player.revivetrigger ) && IsAlive( current_player ) ) { if( self IsTouching( current_player.revivetrigger ) ) { self.in_rt_cached = true; return true; } } } self.in_rt_cached = false; return false; } function get_closest_node( org, nodes ) { return ArrayGetClosest( org, nodes ); } // bars are not damaged pull them off now. function non_destroyed_bar_board_order( origin, chunks ) { //bars = getentarray("bar","script_parameters"); first_bars = []; // first set of 2 bars to be removed first_bars1 = []; // Added single check when combined with wood first_bars2 = []; // Added single check when combined with wood //-------------------------BOARDS---------------------------------------------------------- // If all boards do the old system for( i=0;i 0) { index = temp_array[RandomIntRange(0, temp_array.size)].index; // must pick unbroken piece first! return ents[index]; } else { for( i = 1; i < ents.size; i++ ) { temp_dist = Distance2d( origin, ents[i].origin ); if( temp_dist < dist ) { dist = temp_dist; index = i; } } return ents[index]; } } //edge_fog_start() //{ // playpoint = struct::get( "edge_fog_start", "targetname" ); // // if( !IsDefined( playpoint ) ) // { // // } // // while( isdefined( playpoint ) ) // { // playfx( level._effect["edge_fog"], playpoint.origin ); // // if( !isdefined( playpoint.target ) ) // { // return; // } // // playpoint = struct::get( playpoint.target, "targetname" ); // } //} //chris_p - fix bug with this not being an ent array! function in_playable_area() { playable_area = getentarray("player_volume","script_noteworthy"); if( !IsDefined( playable_area ) ) { /# println( "No playable area playable_area found! Assume EVERYWHERE is PLAYABLE" ); #/ return true; } for(i=0;i=0; i-- ) { decimal = decimal/10 + int(floatParts[1][i])/10; } if ( whole >= 0 ) return (whole + decimal); else return (whole - decimal); } // // TABLE LOOK SECTION ============================================================ // // Read a value from a table and set the related level.zombie_var // function set_zombie_var( zvar, value, is_float, column, is_team_based ) { if ( !IsDefined( is_float ) ) { is_float = false; } if ( !IsDefined(column) ) { column = 1; } if ( IS_TRUE( is_team_based ) ) { foreach( team in level.teams ) { level.zombie_vars[ team ][ zvar ] = value; } } else { level.zombie_vars[zvar] = value; } return value; } // Read a value from a table and return the result // function get_table_var( table, var_name, value, is_float, column ) { if ( !IsDefined(table) ) { table = "mp/zombiemode.csv"; } if ( !IsDefined(is_float) ) { is_float = false; } if ( !IsDefined(column) ) { column = 1; } // First look it up in the table table_value = TableLookUp( table, 0, var_name, column ); if ( IsDefined( table_value ) && table_value != "" ) { if( is_float ) { value = string_to_float( table_value ); } else { value = int( table_value ); } } return value; } function hudelem_count() { /# max = 0; curr_total = 0; while( 1 ) { if( level.hudelem_count > max ) { max = level.hudelem_count; } println( "HudElems: " + level.hudelem_count + "[Peak: " + max + "]" ); WAIT_SERVER_FRAME; } #/ } function debug_round_advancer() { /# while( 1 ) { zombs = zombie_utility::get_round_enemy_array(); for( i = 0; i < zombs.size; i++ ) { zombs[i] dodamage( zombs[i].health + 666, ( 0, 0, 0 ) ); wait 0.5; } } #/ } function print_run_speed( speed ) { /# self endon( "death" ); while( 1 ) { print3d( self.origin +( 0, 0, 64 ), speed, ( 1, 1, 1 ) ); WAIT_SERVER_FRAME; } #/ } function draw_line_ent_to_ent( ent1, ent2 ) { /# if( GetDvarInt( "zombie_debug" ) != 1 ) { return; } ent1 endon( "death" ); ent2 endon( "death" ); while( 1 ) { line( ent1.origin, ent2.origin ); WAIT_SERVER_FRAME; } #/ } function draw_line_ent_to_pos( ent, pos, end_on ) { /# if( GetDvarInt( "zombie_debug" ) != 1 ) { return; } ent endon( "death" ); ent notify( "stop_draw_line_ent_to_pos" ); ent endon( "stop_draw_line_ent_to_pos" ); if( IsDefined( end_on ) ) { ent endon( end_on ); } while( 1 ) { line( ent.origin, pos ); WAIT_SERVER_FRAME; } #/ } function debug_print( msg ) { /# if( GetDvarInt( "zombie_debug" ) > 0 ) { println( "######### ZOMBIE: " + msg ); } #/ } function debug_blocker( pos, rad, height ) { /# self notify( "stop_debug_blocker" ); self endon( "stop_debug_blocker" ); for( ;; ) { if( GetDvarInt( "zombie_debug" ) != 1 ) { return; } WAIT_SERVER_FRAME; drawcylinder( pos, rad, height ); } #/ } function drawcylinder( pos, rad, height ) { /# currad = rad; curheight = height; for( r = 0; r < 20; r++ ) { theta = r / 20 * 360; theta2 = ( r + 1 ) / 20 * 360; line( pos +( cos( theta ) * currad, sin( theta ) * currad, 0 ), pos +( cos( theta2 ) * currad, sin( theta2 ) * currad, 0 ) ); line( pos +( cos( theta ) * currad, sin( theta ) * currad, curheight ), pos +( cos( theta2 ) * currad, sin( theta2 ) * currad, curheight ) ); line( pos +( cos( theta ) * currad, sin( theta ) * currad, 0 ), pos +( cos( theta ) * currad, sin( theta ) * currad, curheight ) ); } #/ } function print3d_at_pos( msg, pos, thread_endon, offset ) { /# self endon( "death" ); if( IsDefined( thread_endon ) ) { self notify( thread_endon ); self endon( thread_endon ); } if( !IsDefined( offset ) ) { offset = ( 0, 0, 0 ); } while( 1 ) { print3d( self.origin + offset, msg ); WAIT_SERVER_FRAME; } #/ } function debug_breadcrumbs() { /# self endon( "disconnect" ); self notify("stop_debug_breadcrumbs"); self endon("stop_debug_breadcrumbs"); while( 1 ) { if( GetDvarInt( "zombie_debug" ) != 1 ) { wait( 1 ); continue; } for( i = 0; i < self.zombie_breadcrumbs.size; i++ ) { drawcylinder( self.zombie_breadcrumbs[i], 5, 5 ); } WAIT_SERVER_FRAME; } #/ } /# function debug_attack_spots_taken() { // this section was totally commented out. self notify("stop_debug_breadcrumbs"); self endon("stop_debug_breadcrumbs"); while( 1 ) { if( GetDvarInt( "zombie_debug" ) != 2 ) { wait( 1 ); continue; } WAIT_SERVER_FRAME; count = 0; for( i = 0; i < self.attack_spots_taken.size; i++ ) { if( self.attack_spots_taken[i] ) { count++; circle(self.attack_spots[i], 12, (1,0,0), false, true, 1); } else { circle(self.attack_spots[i], 12, (0,1,0), false, true, 1); } } msg = "" + count + " / " + self.attack_spots_taken.size; print3d( self.origin, msg ); } } #/ function float_print3d( msg, time ) { /# self endon( "death" ); time = GetTime() + ( time * 1000 ); offset = ( 0, 0, 72 ); while( GetTime() < time ) { offset = offset + ( 0, 0, 2 ); print3d( self.origin + offset, msg, ( 1, 1, 1 ) ); WAIT_SERVER_FRAME; } #/ } function do_player_vo(snd, variation_count) { index = get_player_index(self); // updated to new alias format - Steve G sound = "zmb_vox_plr_" + index + "_" + snd; if(IsDefined (variation_count)) { sound = sound + "_" + randomintrange(0, variation_count); } if(!isDefined(level.player_is_speaking)) { level.player_is_speaking = 0; } if (level.player_is_speaking == 0) { level.player_is_speaking = 1; self PlaySoundWithNotify(sound, "sound_done"); self waittill("sound_done"); //This ensures that there is at least 3 seconds waittime before playing another VO. wait(2); level.player_is_speaking = 0; } } function is_magic_bullet_shield_enabled( ent ) { if( !IsDefined( ent ) ) return false; //return ( IsDefined( ent.magic_bullet_shield ) && ent.magic_bullet_shield == true ); return !IS_TRUE( ent.allowDeath ); } function really_play_2D_sound(sound) { temp_ent = spawn("script_origin", (0,0,0)); temp_ent PlaySoundWithNotify (sound, sound + "wait"); temp_ent waittill (sound + "wait"); WAIT_SERVER_FRAME; temp_ent delete(); } function play_sound_2D(sound) { level thread really_play_2D_sound(sound); /* if(!isdefined(level.playsound2dent)) { level.playsound2dent = spawn("script_origin",(0,0,0)); } //players=GetPlayers(); level.playsound2dent playsound ( sound ); */ /* temp_ent = spawn("script_origin", (0,0,0)); temp_ent PlaySoundWithNotify (sound, sound + "wait"); temp_ent waittill (sound + "wait"); WAIT_SERVER_FRAME; temp_ent delete(); */ } function include_weapon( weapon_name, in_box ) { /# println( "ZM >> include_weapon = " + weapon_name ); #/ if( !isDefined( in_box ) ) { in_box = true; } zm_weapons::include_zombie_weapon( weapon_name, in_box ); } // Allows triggers to be un/seen by players function trigger_invisible( enable ) { players = GetPlayers(); for ( i = 0; i < players.size; i++ ) { if ( isdefined( players[i] ) ) { self SetInvisibleToPlayer( players[i], enable ); } } } // Print3d function print3d_ent( text, color, scale, offset, end_msg, overwrite ) { self endon("death"); if ( IsDefined(overwrite) && overwrite && IsDefined( self._debug_print3d_msg ) ) { // Kill the previous thread self notify( "end_print3d" ); WAIT_SERVER_FRAME; } self endon("end_print3d"); if ( !IsDefined(color) ) { color = (1,1,1); } if ( !IsDefined(scale) ) { scale = 1.0; } if ( !IsDefined(offset) ) { offset = (0,0,0); } if ( IsDefined(end_msg) ) { self endon(end_msg); } // This way you can change the message dynamically by changing the var self._debug_print3d_msg = text; /# while ( !IS_TRUE( level.disable_print3d_ent ) ) { print3d( self.origin+offset, self._debug_print3d_msg, color, scale ); WAIT_SERVER_FRAME; } #/ } // // function create_counter_hud( x ) { if( !IsDefined( x ) ) { x = 0; } hud = create_simple_hud(); hud.alignX = "left"; hud.alignY = "top"; hud.horzAlign = "user_left"; hud.vertAlign = "user_top"; hud.color = ( 1, 1, 1 ); // hud.color = ( 0.21, 0, 0 ); hud.fontscale = 32; hud.x = x; hud.alpha = 0; hud SetShader( "hud_chalk_1", 64, 64 ); return hud; } // // Get the name of the zone that the entity is currently in // self is the entity to check on function get_current_zone( return_zone ) { level flag::wait_till( "zones_initialized" ); if ( IsDefined(self.cached_zone) ) { zone = self.cached_zone; zone_name = self.cached_zone_name; vol = self.cached_zone_volume; // check most recent zone and volume if ( self IsTouching(zone.volumes[vol]) ) { if ( IS_TRUE( return_zone ) ) { return zone; } return zone_name; } // check other volumes in the most recent zone for (i = 0; i < zone.volumes.size; i++) { if ( i == vol ) continue; if ( self IsTouching(zone.volumes[i]) ) { self.cached_zone = zone; self.cached_zone_volume = i; if ( IS_TRUE( return_zone ) ) { return zone; } return zone_name; } } } // clear out active zone flags for( z=0; z= 90 ) { time = 4; } else if( scaled_damage >= 50 ) { time = 3; } else if( scaled_damage >= 25 ) { time = 2; } else if( scaled_damage > 10 ) { time = 1; } if( time ) { if ( !IsDefined( shockType ) ) { shockType = "explosion"; } self ShellShock( shockType, time ); } else if (IsDefined(shockLight)) { self ShellShock( shockLight, time ); } } function increment_ignoreme() { DEFAULT( self.ignorme_count, 0 ); self.ignorme_count++; self.ignoreme = ( self.ignorme_count > 0 ); } function decrement_ignoreme() { DEFAULT( self.ignorme_count, 0 ); if( self.ignorme_count > 0 ) { self.ignorme_count--; } else { AssertMsg( "making ignorme_count less than 0" ); } self.ignoreme = ( self.ignorme_count > 0 ); } // ww: increment the is_drinking variable on a player function increment_is_drinking() { //self endon( "death" ); /# if( IS_TRUE(level.devgui_dpad_watch) ) { self.is_drinking++; return; } #/ if( !isdefined(self.is_drinking) ) self.is_drinking = 0; if( self.is_drinking == 0 ) { self DisableOffhandWeapons(); self DisableWeaponCycling(); } self.is_drinking++; } // ww: checks is_drinking //is_drinking() //{ // //self endon( "death" ); // return ( self.is_drinking > 0 ); //} // to check if more than one is active at once function is_multiple_drinking() { //self endon( "death" ); return ( self.is_drinking > 1 ); } // ww: decrement drinking function decrement_is_drinking() { //self endon( "death" ); if( self.is_drinking > 0 ) { self.is_drinking--; } else { AssertMsg( "making is_drinking less than 0" ); } if( self.is_drinking == 0 ) { self EnableOffhandWeapons(); self EnableWeaponCycling(); } } // ww: clear the variable, used for players going in to last stand function clear_is_drinking() { //self endon( "death" ); self.is_drinking = 0; self EnableOffhandWeapons(); self EnableWeaponCycling(); } // PORTIZ 7/27/16: At this point there are multiple mechanics prolonging solo games when player doesn't have Quick Revive, so we need a // better way of supporting them in conjunction function increment_no_end_game_check() { DEFAULT( level.n_no_end_game_check_count, 0 ); level.n_no_end_game_check_count++; level.no_end_game_check = ( level.n_no_end_game_check_count > 0 ); } function decrement_no_end_game_check() { DEFAULT( level.n_no_end_game_check_count, 0 ); if( level.n_no_end_game_check_count > 0 ) { level.n_no_end_game_check_count--; } else { AssertMsg( "making no_end_game_check_count less than 0" ); } level.no_end_game_check = ( level.n_no_end_game_check_count > 0 ); if ( !level.no_end_game_check ) { level zm::checkForAllDead(); // run this manually any time this is decremented back to a false state } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* function fade_out(time) { if (!isDefined(time)) { time = 1; } if( !IsDefined(level.introblack) ) { level.introblack = NewHudElem(); level.introblack.x = 0; level.introblack.y = 0; level.introblack.horzAlign = "fullscreen"; level.introblack.vertAlign = "fullscreen"; level.introblack.foreground = true; level.introblack SetShader( "black", 640, 480 ); level.introblack.alpha = 0; wait .05; } if( time > 0 ) { level.introblack FadeOverTime( time ); } level.introblack.alpha = 1; players = GetPlayers(); for(i = 0; i < players.size; i++) { players[i] freezecontrols(true); } wait time; } function fade_in( hold_black_time ) { if( !IsDefined(level.introblack) ) { level.introblack = NewHudElem(); level.introblack.x = 0; level.introblack.y = 0; level.introblack.horzAlign = "fullscreen"; level.introblack.vertAlign = "fullscreen"; level.introblack.foreground = true; level.introblack SetShader( "black", 640, 480 ); level.introblack.alpha = 1; wait .05; } level.introblack.alpha = 1; if( IsDefined( hold_black_time ) ) wait hold_black_time; else wait .2; level.introblack FadeOverTime( 1.5 ); level.introblack.alpha = 0; level notify("fade_introblack"); wait 1.5; players = GetPlayers(); for(i = 0; i < players.size; i++) { players[i] freezecontrols(false); } level notify("fade_in_complete"); } */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // WEAPONS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function getWeaponClassZM( weapon ) { assert( isdefined( weapon ) ); if ( !isdefined( weapon ) ) { return undefined; } if ( !isdefined ( level.weaponClassArray ) ) { level.weaponClassArray = []; } if ( isdefined( level.weaponClassArray[weapon] ) ) { return level.weaponClassArray[weapon]; } baseWeaponIndex = GetBaseWeaponItemIndex( weapon ); statsTableName = util::getStatsTableName(); weaponClass = tableLookup( statsTableName, 0, baseWeaponIndex, STATS_TABLE_COL_GROUP); level.weaponClassArray[weapon] = weaponClass; return weaponClass; } function spawn_weapon_model( weapon, model, origin, angles, options ) { if ( !isdefined( model ) ) { model = weapon.worldModel; } weapon_model = spawn( "script_model", origin ); if ( isdefined( angles ) ) { weapon_model.angles = angles; } if ( isdefined( options ) ) { weapon_model useweaponmodel( weapon, model, options ); } else { weapon_model useweaponmodel( weapon, model ); } return weapon_model; } function spawn_buildkit_weapon_model( player, weapon, camo, origin, angles ) { weapon_model = spawn( "script_model", origin ); if ( isdefined( angles ) ) { weapon_model.angles = angles; } upgraded = zm_weapons::is_weapon_upgraded( weapon ); if ( upgraded && (!IsDefined( camo ) || 0 > camo) ) { camo = zm_weapons::get_pack_a_punch_camo_index( undefined ); } weapon_model UseBuildKitWeaponModel( player, weapon, camo, upgraded ); return weapon_model; } function is_player_revive_tool( weapon ) { if ( weapon == level.weaponReviveTool || IS_EQUAL(weapon,self.weaponReviveTool) ) { return true; } return false; } function is_limited_weapon( weapon ) { if ( IsDefined( level.limited_weapons ) && IsDefined( level.limited_weapons[weapon] ) ) { return true; } return false; } function register_lethal_grenade_for_level( weaponname ) { weapon = GetWeapon( weaponname ); if ( is_lethal_grenade( weapon ) ) { return; } if ( !isdefined( level.zombie_lethal_grenade_list ) ) { level.zombie_lethal_grenade_list = []; } level.zombie_lethal_grenade_list[weapon] = weapon; } function is_lethal_grenade( weapon ) { if ( !isdefined( weapon ) || !isdefined( level.zombie_lethal_grenade_list ) ) { return false; } return isdefined(level.zombie_lethal_grenade_list[weapon]); } function is_player_lethal_grenade( weapon ) { if ( !isdefined( weapon ) || !isdefined( self.current_lethal_grenade ) ) { return false; } return self.current_lethal_grenade == weapon; } function get_player_lethal_grenade() { grenade = level.weaponNone; if(IsDefined(self.current_lethal_grenade)) { grenade = self.current_lethal_grenade; } return grenade; } function set_player_lethal_grenade( weapon ) { if ( !IsDefined( weapon ) ) { weapon = level.weaponNone; } self notify( "new_lethal_grenade", weapon ); self.current_lethal_grenade = weapon; } function init_player_lethal_grenade() { self set_player_lethal_grenade( level.zombie_lethal_grenade_player_init ); } function register_tactical_grenade_for_level( weaponname ) { weapon = GetWeapon( weaponname ); if ( is_tactical_grenade( weapon ) ) { return; } if ( !isdefined( level.zombie_tactical_grenade_list ) ) { level.zombie_tactical_grenade_list = []; } level.zombie_tactical_grenade_list[weapon] = weapon; } function is_tactical_grenade( weapon ) { if ( !isdefined( weapon ) || !isdefined( level.zombie_tactical_grenade_list ) ) { return false; } return isdefined(level.zombie_tactical_grenade_list[weapon]); } function is_player_tactical_grenade( weapon ) { if ( !isdefined( weapon ) || !isdefined( self.current_tactical_grenade ) ) { return false; } return self.current_tactical_grenade == weapon; } function get_player_tactical_grenade() { tactical = level.weaponNone; if(IsDefined(self.current_tactical_grenade)) { tactical = self.current_tactical_grenade; } return tactical; } function set_player_tactical_grenade( weapon ) { if ( !IsDefined( weapon ) ) { weapon = level.weaponNone; } self notify( "new_tactical_grenade", weapon ); self.current_tactical_grenade = weapon; } function init_player_tactical_grenade() { self set_player_tactical_grenade( level.zombie_tactical_grenade_player_init ); } function is_placeable_mine( weapon ) { DEFAULT(level.placeable_mines,[]); if ( !isdefined( weapon ) || weapon == level.weaponNone ) { return false; } return isdefined( level.placeable_mines[weapon.name] ); } function is_player_placeable_mine( weapon ) { if ( !isdefined( weapon ) || !isdefined( self.current_placeable_mine ) ) { return false; } return self.current_placeable_mine == weapon; } function get_player_placeable_mine() { placeable_mine = level.weaponNone; if(IsDefined(self.current_placeable_mine)) { placeable_mine = self.current_placeable_mine; } return placeable_mine; } function set_player_placeable_mine( weapon ) { if ( !IsDefined( weapon ) ) { weapon = level.weaponNone; } self notify( "new_placeable_mine", weapon ); self.current_placeable_mine = weapon; } function init_player_placeable_mine() { self set_player_placeable_mine( level.zombie_placeable_mine_player_init ); } function register_melee_weapon_for_level( weaponname ) { weapon = GetWeapon( weaponname ); if ( is_melee_weapon( weapon ) ) { return; } if ( !isdefined( level.zombie_melee_weapon_list ) ) { level.zombie_melee_weapon_list = []; } level.zombie_melee_weapon_list[weapon] = weapon; } function is_melee_weapon( weapon ) { if ( !isdefined( weapon ) || !isdefined( level.zombie_melee_weapon_list ) || ( weapon == GetWeapon( "none" ) ) ) { return false; } return isdefined(level.zombie_melee_weapon_list[weapon]); } function is_player_melee_weapon( weapon ) { if ( !isdefined( weapon ) || !isdefined( self.current_melee_weapon ) ) { return false; } return self.current_melee_weapon == weapon; } function get_player_melee_weapon() { melee_weapon = level.weaponNone; if(IsDefined(self.current_melee_weapon)) { melee_weapon = self.current_melee_weapon; } return melee_weapon; } function set_player_melee_weapon( weapon ) { if ( !IsDefined( weapon ) ) { weapon = level.weaponNone; } self notify( "new_melee_weapon", weapon ); self.current_melee_weapon = weapon; } function init_player_melee_weapon() { self set_player_melee_weapon( level.zombie_melee_weapon_player_init ); } function register_hero_weapon_for_level( weaponname ) { weapon = GetWeapon( weaponname ); if ( is_hero_weapon( weapon ) ) { return; } if ( !isdefined( level.zombie_hero_weapon_list ) ) { level.zombie_hero_weapon_list = []; } level.zombie_hero_weapon_list[weapon] = weapon; } function is_hero_weapon( weapon ) { if ( !isdefined( weapon ) || !isdefined( level.zombie_hero_weapon_list ) ) { return false; } return isdefined(level.zombie_hero_weapon_list[weapon]); } function is_player_hero_weapon( weapon ) { if ( !isdefined( weapon ) || !isdefined( self.current_hero_weapon ) ) { return false; } return self.current_hero_weapon == weapon; } function get_player_hero_weapon() { hero_weapon = level.weaponNone; if(IsDefined(self.current_hero_weapon)) { hero_weapon = self.current_hero_weapon; } return hero_weapon; } function set_player_hero_weapon( weapon ) { if ( !IsDefined( weapon ) ) { weapon = level.weaponNone; } self notify( "new_hero_weapon", weapon ); self.current_hero_weapon = weapon; } function init_player_hero_weapon() { self set_player_hero_weapon( level.zombie_hero_weapon_player_init ); } function has_player_hero_weapon() { return (IsDefined(self.current_hero_weapon) && self.current_hero_weapon != level.weaponNone); } function should_watch_for_emp() { return IS_TRUE( level.should_watch_for_emp ); } function register_offhand_weapons_for_level_defaults() { if ( isdefined( level.register_offhand_weapons_for_level_defaults_override ) ) { [[ level.register_offhand_weapons_for_level_defaults_override ]](); return; } register_lethal_grenade_for_level( "frag_grenade" ); level.zombie_lethal_grenade_player_init = GetWeapon( "frag_grenade" ); register_tactical_grenade_for_level( "cymbal_monkey" ); level.zombie_tactical_grenade_player_init = undefined; level.zombie_placeable_mine_player_init = undefined; register_melee_weapon_for_level( "knife" ); register_melee_weapon_for_level( "bowie_knife" ); level.zombie_melee_weapon_player_init = GetWeapon( "knife" ); level.zombie_equipment_player_init = undefined; } function init_player_offhand_weapons() { init_player_lethal_grenade(); init_player_tactical_grenade(); init_player_placeable_mine(); init_player_melee_weapon(); init_player_hero_weapon(); zm_equipment::init_player_equipment(); } function is_offhand_weapon( weapon ) { return (is_lethal_grenade( weapon ) || is_tactical_grenade( weapon ) || is_placeable_mine( weapon ) || is_melee_weapon( weapon ) || is_hero_weapon( weapon ) || zm_equipment::is_equipment( weapon ) ); } function is_player_offhand_weapon( weapon ) { return (self is_player_lethal_grenade( weapon ) || self is_player_tactical_grenade( weapon ) || self is_player_placeable_mine( weapon ) || self is_player_melee_weapon( weapon ) || self is_player_hero_weapon( weapon ) || self zm_equipment::is_player_equipment( weapon ) ); } function has_powerup_weapon() { return IS_TRUE( self.has_powerup_weapon ); } function has_hero_weapon() { weapon = self GetCurrentWeapon(); return IS_TRUE( weapon.isheroweapon ); } function give_start_weapon( b_switch_weapon ) { //DEFAULT( self.hasCompletedSuperEE, self zm_stats::get_global_stat( "DARKOPS_GENESIS_SUPER_EE" ) > 0 ); /* if( self.hasCompletedSuperEE ) { self zm_weapons::weapon_give( level.start_weapon, false, false, true, false ); self GiveMaxAmmo( level.start_weapon ); self zm_weapons::weapon_give( level.super_ee_weapon, false, false, true, b_switch_weapon ); } else { self zm_weapons::weapon_give( level.start_weapon, false, false, true, b_switch_weapon ); } */ self zm_weapons::weapon_give( level.start_weapon, false, false, true, false ); self GiveMaxAmmo( level.start_weapon ); self zm_weapons::weapon_give( level.super_ee_weapon, false, false, true, b_switch_weapon ); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function array_flag_wait_any( flag_array ) { if( !IsDefined ( level._array_flag_wait_any_calls ) ) { level._n_array_flag_wait_any_calls = 0; } else { level._n_array_flag_wait_any_calls ++; // Used to ensure that we can have multiple calls to this concurrently, that don't interfere with each other. } str_condition = "array_flag_wait_call_" + level._n_array_flag_wait_any_calls; for(index = 0; index < flag_array.size; index ++) { level thread array_flag_wait_any_thread ( flag_array[ index ], str_condition ); } level waittill( str_condition ); } function array_flag_wait_any_thread( flag_name, condition ) { level endon( condition ); level flag::wait_till( flag_name ); level notify( condition ); } function groundpos( origin ) { return bullettrace( origin, ( origin + ( 0, 0, -100000 ) ), 0, self )[ "position" ]; } function groundpos_ignore_water( origin ) { return bullettrace( origin, ( origin + ( 0, 0, -100000 ) ), 0, self, true )[ "position" ]; } function groundpos_ignore_water_new( origin ) { return groundtrace( origin, ( origin + ( 0, 0, -100000 ) ), 0, self, true )[ "position" ]; } /@ "Name: self_delete()" "Summary: Just calls the delete() script command on self. Reason for this is so that we can use array::thread_all to delete entities" "Module: Entity" "CallOn: An entity" "Example: ai[ 0 ] thread self_delete();" "SPMP: singleplayer" @/ function self_delete() { if (IsDefined(self)) { self delete(); } } /@ "Name: ignore_triggers( )" "Summary: Makes the entity that this is threaded on not able to set off triggers for a certain length of time." "Module: Utility" "CallOn: an entity" "Example: guy thread ignore_triggers( 0.2 );" "SPMP: singleplayer" @/ function ignore_triggers( timer ) { // ignore triggers for awhile so others can trigger the trigger we're in. self endon( "death" ); self.ignoreTriggers = true; if( IsDefined( timer ) ) { wait( timer ); } else { wait( 0.5 ); } self.ignoreTriggers = false; } /@ "Name: giveachievement_wrapper( , [all_players] )" "Summary: Gives an Achievement to the specified player" "Module: Mp" "MandatoryArg: : The code string for the achievement" "OptionalArg: [all_players]: If true, then give everyone the achievement" "Example: player giveachievement_wrapper( "MAK_ACHIEVEMENT_RYAN" );" "SPMP: singleplayer" @/ function giveachievement_wrapper( achievement, all_players ) { if ( achievement == "" ) { return; } //don't do stats stuff if stats are disabled if ( IS_TRUE( level.zm_disable_recording_stats ) ) { return; } achievement_lower = tolower(achievement); global_counter = 0; if ( IsDefined( all_players ) && all_players ) { players = GetPlayers(); for ( i = 0; i < players.size; i++ ) { players[i] GiveAchievement( achievement ); has_achievement = false; // T7 DLC5 TODO: players[i] zm_stats::get_global_stat( achievement_lower ); if(!IS_TRUE(has_achievement)) { global_counter++; } // T7 DLC5 TODO: players[i] zm_stats::increment_client_stat( achievement_lower,false ); if( issplitscreen() && i == 0 || !issplitscreen() ) { if(isDefined(level.achievement_sound_func)) { players[i] thread [[level.achievement_sound_func]](achievement_lower); } } } } else { if ( !IsPlayer( self ) ) { /# println( "^1self needs to be a player for util::giveachievement_wrapper()" ); #/ return; } self GiveAchievement( achievement ); //test the stat before updating it from 0 to 1 has_achievement = false; // T7 DLC5 TODO: self zm_stats::get_global_stat( achievement_lower ); if(!IS_TRUE(has_achievement)) { global_counter++; } // T7 DLC5 TODO: self zm_stats::increment_client_stat( achievement_lower,false ); if(isDefined(level.achievement_sound_func)) { self thread [[level.achievement_sound_func]](achievement_lower); } } if(global_counter) { incrementCounter( "global_" + achievement_lower,global_counter); } } function GetYaw(org) { angles = VectorToAngles(org-self.origin); return angles[1]; } function GetYawToSpot(spot) { pos = spot; yaw = self.angles[1] - GetYaw(pos); yaw = AngleClamp180( yaw ); return yaw; } /* ============= ///ScriptDocBegin "Name: disable_react()" "Summary: Disables reaction behavior of given AI" "Module: Utility" "CallOn: An ai" "Example: level.zakhaev disable_react();" "SPMP: singleplayer" ///ScriptDocEnd ============= */ function disable_react() { assert( isalive( self ), "Tried to disable react on a non ai" ); self.a.disableReact = true; self.allowReact = false; } /* ============= ///ScriptDocBegin "Name: enable_react()" "Summary: Enables reaction behavior of given AI" "Module: Utility" "CallOn: An ai" "Example: level.zakhaev enable_react();" "SPMP: singleplayer" ///ScriptDocEnd ============= */ function enable_react() { assert( isalive( self ), "Tried to enable react on a non ai" ); self.a.disableReact = false; self.allowReact = true; } // MikeD (12/15/2007): IW abandoned the auto-adjust feature, however, we can use it for stats? // SCRIPTER_MOD: JesseS (4/14/2008): Added back in for Arcade mode function bullet_attack( type ) { if ( type == "MOD_PISTOL_BULLET" ) { return true; } return type == "MOD_RIFLE_BULLET"; } function pick_up() { player = self.owner; self destroy_ent(); clip_ammo = player GetWeaponAmmoClip( self.weapon ); clip_max_ammo = self.weapon.clipSize; if( clip_ammo < clip_max_ammo ) { clip_ammo++; } player SetWeaponAmmoClip( self.weapon, clip_ammo ); } function destroy_ent() { self delete(); } /@ "Name: waittill_not_moving()" "Summary: waits for the object to stop moving" "Module: Utility" "CallOn: Object that moves like a thrown grenade" "Example: self waittill_not_moving();" "SPMP: singleplayer" @/ function waittill_not_moving() { self endon("death"); self endon( "disconnect" ); self endon( "detonated" ); level endon( "game_ended" ); if ( self.classname == "grenade" ) { self waittill("stationary"); } else { prevorigin = self.origin; while(1) { wait .15; if ( self.origin == prevorigin ) break; prevorigin = self.origin; } } } /@ "Name: get_closest_player( )" "Summary: Returns the closest player to the given origin." "Module: Coop" "MandatoryArg: : The vector to use to compare the distances to" "Example: closest_player = get_closest_player( objective.origin );" "SPMP: singleplayer" @/ function get_closest_player( org ) { players = []; players = GetPlayers(); return ArrayGetClosest( org, players ); } function ent_flag_init_ai_standards() { message_array = []; message_array[ message_array.size ] = "goal"; message_array[ message_array.size ] = "damage"; for( i = 0; i < message_array.size; i++) { self flag::init( message_array[ i ] ); self thread ent_flag_wait_ai_standards( message_array[ i ] ); } } function ent_flag_wait_ai_standards( message ) { /* only runs the first time on the message, so for example if it's waiting on goal, it will only set the goal to true the first time. It also doesn't call ent_set_flag() because that would notify the message possibly twice in the same frame, or worse in the next frame. */ self endon("death"); self waittill( message ); self.ent_flag[ message ] = true; } /@ "Name: flat_angle( )" "Summary: Returns the specified angle as a flat angle.( 45, 90, 30 ) becomes( 0, 90, 0 ). Useful if you just need an angle around Y - axis." "Module: Vector" "CallOn: " "MandatoryArg: : angles to flatten" "Example: yaw = flat_angle( node.angles );" "SPMP: singleplayer" @/ function flat_angle( angle ) { rangle = ( 0, angle[ 1 ], 0 ); return rangle; } /@ "Name: clear_run_anim()" "Summary: Clears any set run anims " "Module: AI" "CallOn: an actor" "Example: guy clear_run_anim();" "SPMP: singleplayer" @/ function clear_run_anim() { self.alwaysRunForward = undefined; self.a.combatrunanim = undefined; self.run_noncombatanim = undefined; self.walk_combatanim = undefined; self.walk_noncombatanim = undefined; self.preCombatRunEnabled = true; } function track_players_intersection_tracker() { self endon( "disconnect" ); self endon( "death" ); level endon( "end_game" ); wait( 5 ); while ( 1 ) { killed_players = false; players = GetPlayers(); for ( i = 0; i < players.size; i++ ) { if ( players[i] laststand::player_is_in_laststand() || "playing" != players[i].sessionstate ) { continue; } for ( j = 0; j < players.size; j++ ) { if ( i == j || players[j] laststand::player_is_in_laststand() || "playing" != players[j].sessionstate ) { continue; } if ( isDefined( level.player_intersection_tracker_override ) ) { if ( players[i] [[level.player_intersection_tracker_override]]( players[j] ) ) { continue; } } playerI_origin = players[i].origin; playerJ_origin = players[j].origin; //Check height first if ( abs(playerI_origin[2] - playerJ_origin[2] ) > 60 ) continue; //Check 2d distance distance_apart = distance2d( playerI_origin, playerJ_origin ); //IPrintLnBold( "player=", i, ",", j, "distance_apart=", distance_apart ); if ( abs(distance_apart) > 18 ) continue; /# IPrintLnBold( "PLAYERS ARE TOO FRIENDLY!!!!!" ); #/ players[i] dodamage( 1000, (0, 0, 0) ); players[j] dodamage( 1000, (0, 0, 0) ); if ( !killed_players ) { players[i] playlocalsound( level.zmb_laugh_alias ); } players[i] zm_stats::increment_map_cheat_stat( "cheat_too_friendly" ); players[i] zm_stats::increment_client_stat( "cheat_too_friendly",false ); players[i] zm_stats::increment_client_stat( "cheat_total",false ); players[j] zm_stats::increment_map_cheat_stat( "cheat_too_friendly" ); players[j] zm_stats::increment_client_stat( "cheat_too_friendly",false ); players[j] zm_stats::increment_client_stat( "cheat_total",false ); killed_players = true; } } wait( .5 ); } } /@ "Name: is_player_looking_at( , , )" "Summary: Checks to see if the player can dot and trace to a point" "Module: Player" "CallOn: A Player" "MandatoryArg: The position you're checking if the player is looking at" "OptionalArg: Optional override dot (between 0 and 1) the higher the number, the more the player has to be looking right at the spot." "OptionalArg: Set to false to skip the bullet trace check" "OptionalArg: Ignore ent passed to trace check" "Example: if ( GetPlayers()[0] is_player_looking_at( org.origin ) )" "SPMP: zombies" @/ function is_player_looking_at(origin, dot, do_trace, ignore_ent) { assert(IsPlayer(self), "player_looking_at must be called on a player."); if (!IsDefined(dot)) { dot = .7; } if (!IsDefined(do_trace)) { do_trace = true; } eye = self util::get_eye(); delta_vec = AnglesToForward(VectorToAngles(origin - eye)); view_vec = AnglesToForward(self GetPlayerAngles()); new_dot = VectorDot( delta_vec, view_vec ); if ( new_dot >= dot ) { if (do_trace) { return BulletTracePassed( origin, eye, false, ignore_ent ); } else { return true; } } return false; } /@ "Name: add_gametype()" "Summary: dummy - Rex looks for these to populate the gametype pulldown" "SPMP: zombies" @/ function add_gametype( gt, dummy1, name, dummy2 ) { } /@ "Name: add_gameloc()" "Summary: dummy - Rex looks for these to populate the location pulldown" "SPMP: zombies" @/ function add_gameloc( gl, dummy1, name, dummy2 ) { } /@ "Name: get_closest_index( , , )" "Summary: same as getClosest but returns the closest entity's array index instead of the actual entity." "Module: Distance" "CallOn: " "MandatoryArg: : Origin to be closest to." "MandatoryArg: : Array of entities to check distance on." "OptionalArg: : Maximum distance to check" "Example: " "SPMP: mp" @/ function get_closest_index( org, array, dist ) { if( !IsDefined( dist ) ) { dist = 9999999; } distsq = dist*dist; if( array.size < 1 ) { return; } index = undefined; for( i = 0;i < array.size;i++ ) { newdistsq = distancesquared( array[ i ].origin, org ); if( newdistsq >= distsq ) { continue; } distsq = newdistsq; index = i; } return index; } function is_valid_zombie_spawn_point(point) { liftedorigin = point.origin + (0,0,5); size = 48; height = 64; mins = (-1 * size,-1 * size,0 ); maxs = ( size,size,height ); absmins = liftedorigin + mins; absmaxs = liftedorigin + maxs; // check to see if we would telefrag any players if ( BoundsWouldTelefrag( absmins, absmaxs ) ) { return false; } return true; } /@ "Name: get_closest_index( , , )" "Summary: same as getClosest but returns the closest entity's array index instead of the actual entity." "Module: Distance" "CallOn: " "MandatoryArg: : Origin to be closest to." "MandatoryArg: : Array of entities to check distance on." "OptionalArg: : Maximum distance to check" "Example: " "SPMP: mp" @/ function get_closest_index_to_entity( entity, array, dist, extra_check ) { org = entity.origin; if( !IsDefined( dist ) ) { dist = 9999999; } distsq = dist*dist; if( array.size < 1 ) { return; } index = undefined; for( i = 0;i < array.size;i++ ) { if (isdefined(extra_check) && ![[extra_check]](entity,array[ i ]) ) continue; newdistsq = distancesquared( array[ i ].origin, org ); if( newdistsq >= distsq ) { continue; } distsq = newdistsq; index = i; } return index; } function set_gamemode_var(gvar, val) { if(!isdefined(game["gamemode_match"])) { game["gamemode_match"] = []; } game["gamemode_match"][gvar] = val; } function set_gamemode_var_once(gvar, val) { if(!isdefined(game["gamemode_match"])) { game["gamemode_match"] = []; } if(!isdefined(game["gamemode_match"][gvar])) { game["gamemode_match"][gvar] = val; } } function set_game_var(gvar, val) { game[gvar] = val; } function set_game_var_once(gvar, val) { if(!isdefined(game[gvar])) { game[gvar] = val; } } function get_game_var( gvar ) { if(isdefined(game[gvar])) { return game[gvar]; } return undefined; } function get_gamemode_var( gvar ) { if(isdefined(game["gamemode_match"]) && isdefined(game["gamemode_match"][gvar])) { return game["gamemode_match"][gvar]; } return undefined; } function waittill_subset(min_num, string1, string2, string3, string4, string5 ) { self endon ("death"); ent = SpawnStruct(); ent.threads = 0; returned_threads = 0; if (IsDefined (string1)) { self thread util::waittill_string(string1, ent); ent.threads++; } if (IsDefined (string2)) { self thread util::waittill_string(string2, ent); ent.threads++; } if (IsDefined (string3)) { self thread util::waittill_string(string3, ent); ent.threads++; } if (IsDefined (string4)) { self thread util::waittill_string(string4, ent); ent.threads++; } if (IsDefined (string5)) { self thread util::waittill_string(string5, ent); ent.threads++; } while (ent.threads) { ent waittill ("returned"); ent.threads--; returned_threads++; if(returned_threads >= min_num ) //if we've got the minimum number of waittills returned, then that is good enough! { break; } } ent notify ("die"); } function is_headshot( weapon, sHitLoc, sMeansOfDeath ) { if( !isdefined( sHitLoc ) ) { return false; } if( (sHitLoc != "head" && sHitLoc != "helmet") ) { return false; } //ballsitic knives if( sMeansofDeath == "MOD_IMPACT" && weapon.isBallisticKnife ) { return true; } return sMeansOfDeath != "MOD_MELEE" && sMeansOfDeath != "MOD_IMPACT" && sMeansofDeath != "MOD_UNKNOWN"; } function is_jumping() { // this probably doesn't work correctly on the bus // checking PMF_JUMPING in code would give more accurate results ground_ent = self GetGroundEnt(); return (!isdefined(ground_ent)); } function is_explosive_damage( mod ) { if( !IsDefined( mod ) ) return false; if( mod == "MOD_GRENADE" || mod == "MOD_GRENADE_SPLASH" || mod == "MOD_PROJECTILE" || mod == "MOD_PROJECTILE_SPLASH" || mod == "MOD_EXPLOSIVE" ) return true; return false; } function sndSwitchAnnouncerVox( who ) { switch( who ) { case "sam": game["zmbdialog"]["prefix"] = "vox_zmba_sam"; level.zmb_laugh_alias = "zmb_laugh_sam"; level.sndAnnouncerIsRich = false; break; } } function do_player_general_vox(category,type,timer,chance) { if( isDefined(timer) && isDefined(level.votimer[type]) && level.votimer[type] > 0) { return; } self thread zm_audio::create_and_play_dialog( category, type); if(isDefined(timer)) { level.votimer[type] = timer; level thread general_vox_timer( level.votimer[type],type ); } } function general_vox_timer(timer,type) { level endon("end_game"); /# println( "ZM >> VOX TIMER STARTED FOR " + type + " ( " + timer + ")" ); #/ while(timer > 0 ) { wait(1); timer--; } level.votimer[type] = timer; /# println( "ZM >> VOX TIMER ENDED FOR " + type + " ( " + timer + ")" ); #/ } function create_vox_timer(type) { level.votimer[type] = 0; } function play_vox_to_player(category,type,force_variant ) { //self thread zm_audio::playVoxToPlayer( category, type, force_variant ); } function is_favorite_weapon(weapon_to_check) { if(!isDefined(self.favorite_wall_weapons_list)) { return false; } foreach(weapon in self.favorite_wall_weapons_list) { if (weapon_to_check == weapon ) { return true; } } return false; } function add_vox_response_chance(event,chance) { level.response_chances[event] = chance; } function set_demo_intermission_point() { spawnpoints = getentarray("mp_global_intermission", "classname"); if ( !spawnpoints.size ) { return; } spawnpoint = spawnpoints[0]; match_string = ""; location = level.scr_zm_map_start_location; if ( (location == "default" || location == "") && IsDefined( level.default_start_location ) ) { location = level.default_start_location; } match_string = level.scr_zm_ui_gametype + "_" + location; for ( i = 0; i < spawnpoints.size; i++ ) { if ( IsDefined( spawnpoints[i].script_string ) ) { tokens = strtok( spawnpoints[i].script_string," " ); foreach ( token in tokens ) { if ( token == match_string ) { spawnpoint = spawnpoints[i]; i = spawnpoints.size; // to get out of the outer for loop break; } } } } setDemoIntermissionPoint( spawnpoint.origin, spawnpoint.angles ); } function register_map_navcard(navcard_on_map,navcard_needed_for_computer) { level.navcard_needed = navcard_needed_for_computer; level.map_navcard = navcard_on_map; } function does_player_have_map_navcard(player) { return player zm_stats::get_global_stat( level.map_navcard ); } function does_player_have_correct_navcard(player) { if(!isDefined(level.navcard_needed )) { return false; } return player zm_stats::get_global_stat( level.navcard_needed ); } function place_navcard( str_model, str_stat, org, angles ) { navcard = spawn("script_model",org); navcard setmodel( str_model ); navcard.angles = angles; wait 1; // delay between spawning card and picking it up navcard_pickup_trig = Spawn( "trigger_radius_use", org, 0, 84, 72 ); navcard_pickup_trig SetCursorHint( "HINT_NOICON" ); navcard_pickup_trig SetHintString( &"ZOMBIE_NAVCARD_PICKUP" ); navcard_pickup_trig TriggerIgnoreTeam(); a_navcard_stats = array( "navcard_held_zm_transit", "navcard_held_zm_highrise", "navcard_held_zm_buried" ); is_holding_card = false; str_placing_stat = undefined; while(1) { navcard_pickup_trig waittill("trigger",who ); if( is_player_valid(who) ) { // check for any currently held card. If found, clear that stat and save which it was foreach ( str_cur_stat in a_navcard_stats ) { if ( who zm_stats::get_global_stat( str_cur_stat ) ) { str_placing_stat = str_cur_stat; is_holding_card = true; who zm_stats::set_global_stat( str_cur_stat, 0 ); } } who playsound( "zmb_buildable_piece_add" ); who zm_stats::set_global_stat( str_stat, 1 ); who.navcard_grabbed = str_stat; util::wait_network_frame(); is_stat = who zm_stats::get_global_stat( str_stat ); thread sq_refresh_player_navcard_hud(); break; } } navcard delete(); navcard_pickup_trig delete(); // if a card was held, place a new card with the stat of the one that was held (set down the old card) if ( is_holding_card ) { level thread place_navcard( str_model, str_placing_stat, org, angles ); } } function sq_refresh_player_navcard_hud() { if (!IsDefined(level.navcards)) return; players = GetPlayers(); foreach( player in players ) { player thread sq_refresh_player_navcard_hud_internal(); } } function sq_refresh_player_navcard_hud_internal() { self endon("disconnect"); navcard_bits = 0; for(i = 0;i < level.navcards.size; i++) { hasit = self zm_stats::get_global_stat( level.navcards[i] ); if (isdefined(self.navcard_grabbed) && self.navcard_grabbed == level.navcards[i] ) hasit = 1; if( hasit ) { navcard_bits +=( 1 << i ); } } util::wait_network_frame(); self clientfield::set( "navcard_held", 0 ); if ( navcard_bits > 0 ) { util::wait_network_frame(); self clientfield::set( "navcard_held", navcard_bits ); } } function disable_player_move_states(forceStanceChange) { self AllowCrouch( true ); self AllowLean( false ); self AllowAds( false ); self AllowSprint( false ); self AllowProne( false ); self AllowMelee( false ); if((isdefined(forceStanceChange)) && (forceStanceChange == true)) { if ( self GetStance() == "prone" ) { self SetStance( "crouch" ); } } } function enable_player_move_states() { if((!isdefined(self._allow_lean)) || (self._allow_lean == true)) { self AllowLean( true ); } if((!isdefined(self._allow_ads)) || (self._allow_ads == true)) { self AllowAds( true ); } if((!isdefined(self._allow_sprint)) || (self._allow_sprint == true)) { self AllowSprint( true ); } if((!isdefined(self._allow_prone)) || (self._allow_prone == true)) { self AllowProne( true ); } if((!isdefined(self._allow_melee)) || (self._allow_melee == true)) { self AllowMelee( true ); } } function check_and_create_node_lists() { if(!isdefined(level._link_node_list)) { level._link_node_list = []; } if(!isdefined(level._unlink_node_list)) { level._unlink_node_list = []; } } function link_nodes(a, b, bDontUnlinkOnMigrate = false) { if(NodesAreLinked(a,b)) { return; } check_and_create_node_lists(); a_index_string = "" + a.origin; b_index_string = "" + b.origin; if(!isdefined(level._link_node_list[a_index_string])) { level._link_node_list[a_index_string] = spawnstruct(); level._link_node_list[a_index_string].node = a; level._link_node_list[a_index_string].links = []; level._link_node_list[a_index_string].ignore_on_migrate = []; } if(!isdefined(level._link_node_list[a_index_string].links[b_index_string])) { level._link_node_list[a_index_string].links[b_index_string] = b; // Add a record of the link. level._link_node_list[a_index_string].ignore_on_migrate[b_index_string] = bDontUnlinkOnMigrate; } if(isdefined(level._unlink_node_list[a_index_string])) { if(isdefined(level._unlink_node_list[a_index_string].links[b_index_string])) { level._unlink_node_list[a_index_string].links[b_index_string] = undefined; // Remove record of earlier unlink level._unlink_node_list[a_index_string].ignore_on_migrate[b_index_string] = undefined; } } // /# println("Linking node at " + a.origin + " to node at " + b.origin); #/ LinkNodes(a,b); } function unlink_nodes(a,b, bDontLinkOnMigrate = false) { if(!NodesAreLinked(a,b)) { return; } check_and_create_node_lists(); a_index_string = "" + a.origin; b_index_string = "" + b.origin; if(!isdefined(level._unlink_node_list[a_index_string])) { level._unlink_node_list[a_index_string] = spawnstruct(); level._unlink_node_list[a_index_string].node = a; level._unlink_node_list[a_index_string].links = []; level._unlink_node_list[a_index_string].ignore_on_migrate = []; } if(!isdefined(level._unlink_node_list[a_index_string].links[b_index_string])) { level._unlink_node_list[a_index_string].links[b_index_string] = b; // Add a record of the unlink. level._unlink_node_list[a_index_string].ignore_on_migrate[b_index_string] = bDontLinkOnMigrate; } if(isdefined(level._link_node_list[a_index_string])) { if(isdefined(level._link_node_list[a_index_string].links[b_index_string])) { level._link_node_list[a_index_string].links[b_index_string] = undefined; // Remove record of earlier link. level._link_node_list[a_index_string].ignore_on_migrate[b_index_string] = undefined; } } // /# println("Unlinking node at " + a.origin + " from node at " + b.origin); #/ UnlinkNodes(a,b); } //spawn_path_node( (-6392, 4329, 0), (0,0,0), "targetname", "blah" ); function spawn_path_node(origin, angles, k1, v1, k2, v2) { if(!isdefined(level._spawned_path_nodes)) { level._spawned_path_nodes = []; } node = spawnstruct(); node.origin = origin; node.angles = angles; node.k1 = k1; node.v1 = v1; node.k2 = k2; node.v2 = v2; node.node = spawn_path_node_internal(origin, angles, k1, v1, k2, v2); level._spawned_path_nodes[level._spawned_path_nodes.size] = node; return node.node; } function spawn_path_node_internal(origin, angles, k1, v1, k2, v2) { if(isdefined(k2)) { return SpawnPathNode("node_pathnode", origin, angles, k1, v1, k2, v2); } else if(isdefined(k1)) { return SpawnPathNode("node_pathnode", origin, angles, k1, v1); } else { return SpawnPathNode("node_pathnode", origin, angles); } return undefined; } function delete_spawned_path_nodes() { /* if(!isdefined(level._spawned_path_nodes)) { return; } //for(i = 0; i < level._spawned_path_nodes.size; i ++) for(i = level._spawned_path_nodes.size - 1; i > -1; i --) { /# println("Deleting spawned path node @ " + level._spawned_path_nodes[i].origin); #/ DeletePathNode(level._spawned_path_nodes[i].node); level._spawned_path_nodes[i].node = undefined; }*/ } function respawn_path_nodes() { if(!isdefined(level._spawned_path_nodes)) { return; } for(i = 0; i < level._spawned_path_nodes.size; i ++) { node_struct = level._spawned_path_nodes[i]; /# println("Re-spawning spawned path node @ " + node_struct.origin); #/ node_struct.node = spawn_path_node_internal(node_struct.origin, node_struct.angles, node_struct.k1, node_struct.v1, node_struct.k2, node_struct.v2); } } function link_changes_internal_internal(list, func) { keys = GetArrayKeys(list); for(i = 0; i < keys.size; i ++) { node = list[keys[i]].node; node_keys = GetArrayKeys(list[keys[i]].links); for(j = 0; j < node_keys.size; j ++) { if(isdefined(list[keys[i]].links[node_keys[j]])) { if(IS_TRUE(list[keys[i]].ignore_on_migrate[node_keys[j]])) { /# println("Node at " + keys[i] + " to node at " + node_keys[j] + " - IGNORED"); #/ } else { /# println("Node at " + keys[i] + " to node at " + node_keys[j]); #/ [[func]](node, list[keys[i]].links[node_keys[j]]); } } } } } function link_changes_internal(func_for_link_list, func_for_unlink_list) { if(isdefined(level._link_node_list)) { /# println("Link List"); #/ link_changes_internal_internal(level._link_node_list, func_for_link_list); } if(isdefined(level._unlink_node_list)) { /# println("UnLink List"); #/ link_changes_internal_internal(level._unlink_node_list, func_for_unlink_list); } } function link_nodes_wrapper(a, b) { if(!NodesAreLinked(a,b)) { LinkNodes(a,b); } } function unlink_nodes_wrapper(a, b) { if(NodesAreLinked(a,b)) { UnlinkNodes(a,b); } } function undo_link_changes() { /# println("***"); println("***"); println("*** Undoing link changes"); #/ link_changes_internal( &unlink_nodes_wrapper, &link_nodes_wrapper); delete_spawned_path_nodes(); } function redo_link_changes() { /# println("***"); println("***"); println("*** Redoing link changes"); #/ respawn_path_nodes(); link_changes_internal( &link_nodes_wrapper, &unlink_nodes_wrapper); } function is_gametype_active( a_gametypes ) { b_is_gametype_active = false; if ( !IsArray( a_gametypes ) ) { a_gametypes = Array( a_gametypes ); } for ( i = 0; i < a_gametypes.size; i++ ) { if ( GetDvarString( "g_gametype" ) == a_gametypes[ i ] ) { b_is_gametype_active = true; } } return b_is_gametype_active; } function register_custom_spawner_entry( spot_noteworthy, func ) { if ( !IsDefined( level.custom_spawner_entry ) ) { level.custom_spawner_entry = []; } level.custom_spawner_entry[ spot_noteworthy ] = func; } function get_player_weapon_limit( player ) { if ( IsDefined( self.get_player_weapon_limit ) ) { return [[self.get_player_weapon_limit]]( player ); } if ( IsDefined( level.get_player_weapon_limit ) ) { return [[level.get_player_weapon_limit]]( player ); } weapon_limit = 2; if ( player HasPerk( PERK_ADDITIONAL_PRIMARY_WEAPON ) ) { weapon_limit = level.additionalprimaryweapon_limit; } return weapon_limit; } function get_player_perk_purchase_limit() { n_perk_purchase_limit_override = level.perk_purchase_limit; // start with the default value // level-specific override if ( IsDefined( level.get_player_perk_purchase_limit ) ) { n_perk_purchase_limit_override = self [[level.get_player_perk_purchase_limit]](); } return n_perk_purchase_limit_override; } function can_player_purchase_perk() { if ( self.num_perks < self zm_utility::get_player_perk_purchase_limit() ) { return true; } // you can always buy one more if you currently have unquenchable if ( self bgb::is_enabled( "zm_bgb_unquenchable" ) || self bgb::is_enabled( "zm_bgb_soda_fountain" ) ) { return true; } return false; } // Give player all perks // b_exclude_quick_revive - if you need to prevent a solo player from getting quick revive function give_player_all_perks( b_exclude_quick_revive = false ) // self == player { // give the player all the perks in the map a_str_perks = GetArrayKeys( level._custom_perks ); foreach( str_perk in a_str_perks ) { if ( str_perk == PERK_QUICK_REVIVE && b_exclude_quick_revive ) { continue; } if( !self HasPerk( str_perk ) ) { self zm_perks::give_perk( str_perk, false ); if ( isdefined( level.perk_bought_func ) ) { self [[ level.perk_bought_func ]]( str_perk ); } } } } function wait_for_attractor_positions_complete() { self waittill( "attractor_positions_generated" ); self.attract_to_origin = false; } // Migrated from _zm_weapons. function get_player_index( player ) { assert( IsPlayer( player ) ); assert( IsDefined( player.characterIndex ) ); /# // used for testing to switch player's VO in-game from devgui if ( player.entity_num == 0 && GetDvarString( "zombie_player_vo_overwrite" ) != "" ) { new_vo_index = GetDvarInt( "zombie_player_vo_overwrite" ); return new_vo_index; } #/ return player.characterIndex; } // **************************************** // get_specific_character() returns the player character associated with the index given. // For zm_zod, the indices are defined as follows: // #define FLOYD 0 // #define JACK 1 // #define ROSE 2 // #define NERO 3 // For other levels, indices will differ. // Copied from zm_factory.gsc function get_specific_character( n_character_index ) { foreach( character in level.players ) { if( character.characterIndex == n_character_index ) return character; } return undefined; } function zombie_goto_round( n_target_round ) { level notify( "restart_round" ); if ( n_target_round < 1 ) { n_target_round = 1; } level.zombie_total = 0; zombie_utility::ai_calculate_health( n_target_round ); zm::set_round_number( n_target_round - 1 ); // kill all active zombies zombies = zombie_utility::get_round_enemy_array(); if ( isdefined( zombies ) ) { array::run_all( zombies, &Kill ); } level.sndGotoRoundOccurred = true; level waittill( "between_round_over" ); } // spawn ent at v_point and test if it's touching any enabled zone volume; return true/false // this is a workaround for not having a good way of testing whether a point or struct is inside a volume for now // ignore_zone - zone that we don't want to return true from function is_point_inside_enabled_zone( v_origin, ignore_zone ) { temp_ent = Spawn( "script_origin", v_origin ); foreach( zone in level.zones ) { // If the zone hasn't been enabled, don't even bother checking if ( !zone.is_enabled ) { continue; } if( isdefined( ignore_zone ) && ( zone == ignore_zone ) ) { continue; } // Okay check to see if an entity is in one of the zone volumes foreach( e_volume in zone.volumes ) { if ( temp_ent IsTouching( e_volume ) ) { temp_ent Delete(); return true; } } } temp_ent Delete(); return false; } function clear_streamer_hint() { if ( IsDefined(self.streamer_hint) ) { self.streamer_hint Delete(); self.streamer_hint = undefined; } self notify("wait_clear_streamer_hint"); } function wait_clear_streamer_hint( lifetime ) { self endon("wait_clear_streamer_hint"); wait lifetime; if ( IsDefined(self) ) self clear_streamer_hint(); } function create_streamer_hint( origin, angles, value, lifetime ) { if ( self == level ) { foreach( player in GetPlayers() ) { player clear_streamer_hint(); } } self clear_streamer_hint(); self.streamer_hint = CreateStreamerHint( origin, value ); if ( IsDefined(angles) ) self.streamer_hint.angles = angles; if ( self != level ) { self.streamer_hint SetInvisibleToAll(); self.streamer_hint SetVisibleToPlayer( self ); } self.streamer_hint SetIncludeMeshes( true ); self notify("wait_clear_streamer_hint"); if ( IsDefined(lifetime) && lifetime > 0 ) { self thread wait_clear_streamer_hint(lifetime); } } function approximate_path_dist( player ) { AIProfile_BeginEntry( "approximate_path_dist" ); goal_pos = player.origin; if ( isdefined( player.last_valid_position ) ) { goal_pos = player.last_valid_position; } if ( IS_TRUE( player.b_teleporting ) ) { if ( isdefined( player.teleport_location ) ) { goal_pos = player.teleport_location; if ( !IsPointOnNavmesh( goal_pos, self ) ) { position = GetClosestPointOnNavMesh( goal_pos, 100, 15 ); if ( IsDefined( position ) ) { goal_pos = position; } } } } assert( IsDefined( level.pathdist_type ), "level.pathdist_type must be set before calling PathDistance" ); //PathDistance( , , , , , ) approx_dist = PathDistance( self.origin, goal_pos, true, self, level.pathdist_type ); AIProfile_EndEntry(); return approx_dist; } // Helper function for slowdowns function register_slowdown( str_type, n_rate, n_duration ) { if ( !isdefined( level.a_n_slowdown_rates ) ) { level.a_n_slowdown_rates = []; } level.a_s_slowdowns[ str_type ] = SpawnStruct(); level.a_s_slowdowns[ str_type ].n_rate = n_rate; level.a_s_slowdowns[ str_type ].n_duration = n_duration; } // All-purpose function to slow a zombie. Can handle slowdowns from multiple sources. // The slowest rate will always be applied first, until it wears out, then the next highest one will be applied. // str_type is the reason for the slow // n_rate is the new animation rate function slowdown_ai( str_type ) { self notify( "starting_slowdown_ai" ); self endon( "starting_slowdown_ai" ); self endon( "death" ); Assert( isdefined( level.a_s_slowdowns[ str_type ] ), "Slowdown \"" + str_type + "\" must be registered through register_slowdown first" ); if ( !isdefined( self.a_n_slowdown_timeouts ) ) { self.a_n_slowdown_timeouts = []; } n_time = GetTime(); n_timeout = n_time + level.a_s_slowdowns[ str_type ].n_duration; // See if the slowdown is already being applied if ( !isdefined( self.a_n_slowdown_timeouts[ str_type ] ) || self.a_n_slowdown_timeouts[ str_type ] < n_timeout ) { self.a_n_slowdown_timeouts[ str_type ] = n_timeout; } // search for the lowest rate and apply that while ( self.a_n_slowdown_timeouts.size ) { str_lowest_type = undefined; n_lowest_rate = 10.0; foreach( str_index, n_slowdown_timeout in self.a_n_slowdown_timeouts ) { //TODO Make sure clearing an entry doesn't mess up the foreach // remove old expired times if ( n_slowdown_timeout <= n_time ) { self.a_n_slowdown_timeouts[ str_index ] = undefined; continue; } if ( level.a_s_slowdowns[ str_index ].n_rate < n_lowest_rate ) { str_lowest_type = str_index; n_lowest_rate = level.a_s_slowdowns[ str_index ].n_rate; } } if ( isdefined ( str_lowest_type ) ) { // Apply the slowest rate self ASMSetAnimationRate( n_lowest_rate ); n_duration = self.a_n_slowdown_timeouts[ str_lowest_type ] - n_time; wait( n_duration ); // Remove slowdown self.a_n_slowdown_timeouts[ str_lowest_type ] = undefined; } } // No more slowdowns, return to normal self ASMSetAnimationRate( 1.0 ); } //***************************************************************************** //***************************************************************************** function get_player_closest_to( e_target ) { a_players= ArrayCopy( level.activeplayers ); // If the target is also a player, remove it from the copied array of players. ArrayRemoveValue( a_players, e_target ); e_closest_player = ArrayGetClosest( e_target.origin, a_players ); return e_closest_player; } //***************************************************************************** //***************************************************************************** function is_facing( facee, requiredDot = 0.5, b_2d = true ) { orientation = self getPlayerAngles(); v_forward = anglesToForward( orientation ); v_to_facee = facee.origin - self.origin; if( b_2d ) { v_forward_computed = ( v_forward[0], v_forward[1], 0 ); v_to_facee_computed = ( v_to_facee[0], v_to_facee[1], 0 ); } else { v_forward_computed = v_forward; v_to_facee_computed = v_to_facee; } v_unit_forward_computed = VectorNormalize( v_forward_computed ); v_unit_to_facee_computed = VectorNormalize( v_to_facee_computed ); dotProduct = VectorDot( v_unit_forward_computed, v_unit_to_facee_computed ); return ( dotProduct > requiredDot ); // reviver is facing player within given dot tolerance. } //Returns true if the player is in a solo game that cannot be hotjoined function is_solo_ranked_game() { return ( level.players.size == 1 && GetDvarInt( "zm_private_rankedmatch", 0 ) ); } function upload_zm_dash_counters( force_upload = false ) {} function upload_zm_dash_counters_end_game() {} function increment_zm_dash_counter( counter_name, amount ) {} function zm_dash_stats_game_start() {} function zm_dash_stats_game_end() {} function zm_dash_stats_wait_for_consumable_use() {}