5017 lines
141 KiB
Text
5017 lines
141 KiB
Text
#include maps\_anim;
|
|
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\pel2_util;
|
|
#include maps\_vehicle_utility;
|
|
#include maps\see2_threat;
|
|
#include maps\_grenade_toss;
|
|
#include maps\_music;
|
|
#include maps\see2_vehicle_behavior;
|
|
#include maps\see2_fortifications;
|
|
#include maps\see2_sound;
|
|
#include maps\_busing;
|
|
|
|
main()
|
|
{
|
|
|
|
// precache turret for popping and intermediate damage states
|
|
precachemodel("vehicle_ger_tracked_panzer4_d_turret");
|
|
precachemodel("vehicle_ger_tracked_panther_d_turret");
|
|
precachemodel("vehicle_ger_tracked_king_tiger_d_turret");
|
|
precachemodel("vehicle_ger_tracked_panzer4v1_dmg1");
|
|
precachemodel("vehicle_ger_tracked_panther_dmg1");
|
|
precachemodel("vehicle_ger_tracked_king_tiger_dmg1");
|
|
precachemodel("katyusha_rocket");
|
|
|
|
precachemodel("weapon_rus_reznov_knife");
|
|
precachemodel("weapon_rus_mosinnagant_rifle");
|
|
|
|
// precache rumble
|
|
precacheRumble( "tank_damage_heavy_mp" );
|
|
precacheRumble( "tank_damage_light_mp" );
|
|
|
|
level.campaign = "russian";
|
|
|
|
// All vehicles setup
|
|
maps\_see2_panther::main( "vehicle_ger_tracked_panther" );
|
|
maps\_see2_ot34::main( "vehicle_rus_tracked_ot34" );
|
|
maps\_see2_t34::main( "vehicle_rus_tracked_t34" );
|
|
maps\_see2_physics_t34::main( "vehicle_rus_tracked_ot34" );
|
|
maps\_see2_tiger::main( "vehicle_ger_tracked_king_tiger" );
|
|
maps\_see2_panzeriv::main( "vehicle_ger_tracked_panzer4v1" );
|
|
maps\_truck::main( "vehicle_ger_wheeled_covered_opel_blitz", "opel" );
|
|
maps\_see2_flak88::main( "artillery_ger_flak88" );
|
|
maps\_aircraft::main( "vehicle_rus_airplane_il2", "il2", 0, false ); // cut down from 3 turrets to 0
|
|
|
|
precachemodel( "weapon_machinegun_tiger" );
|
|
precachemodel( "vehicle_rus_tracked_ot34_d" );
|
|
precacheturret( "allied_hull_flamethrower" );
|
|
|
|
precachestring( &"SEE2_FLAMETHROWER_HINT" );
|
|
precachestring( &"SEE2_MG_COOP_HINT" );
|
|
precachestring( &"SEE2_ADS_HINT" );
|
|
|
|
|
|
// Setup start points - Currently gets the level logically to this state does not warp players yet
|
|
default_start( ::field_begin );
|
|
add_start( "radio_tower", ::radio_tower_begin, &"STARTS_SEE2_RADIOTOWER" );
|
|
add_start( "fuel_depot", ::fuel_depot_begin, &"STARTS_SEE2_FUELDEPOT" );
|
|
add_start( "air_strike", ::air_strike_begin, &"STARTS_SEE2_AIRSTRIKE" );
|
|
|
|
//maps\_drones::init();
|
|
|
|
level.drones_per_wave = 8;
|
|
|
|
setup_level();
|
|
|
|
maps\_drone::init();
|
|
|
|
level.max_drones["allies"] = 32;
|
|
level.max_drones["axis"] = 32;
|
|
|
|
maps\see2_fx::main();
|
|
maps\see2_drones::main();
|
|
maps\see2_anim::main();
|
|
maps\see2_breadcrumbs::main();
|
|
maps\see2_amb::main();
|
|
|
|
maps\_load::main();
|
|
|
|
level thread difficulty_scale();
|
|
level init_level_flags();
|
|
//-- LEVEL THROTTLING SPAWN FUNCTION VARIABLES
|
|
thread tune_max_ai_numbers();
|
|
|
|
//character\char_ger_pnzgren_k98::precache();
|
|
//character\char_rus_r_rifle::precache();
|
|
|
|
// These are called everytime a drone is spawned in to set up the character.
|
|
//level.drone_spawnFunction["axis"] = character\char_ger_pnzgren_k98::main;
|
|
//level.drone_spawnFunction["allies"] = character\char_rus_r_rifle::main;
|
|
|
|
level tune_ai();
|
|
|
|
level.enemy_armor = [];
|
|
level.death_bucket = [];
|
|
level.enemy_infantry = [];
|
|
level.line_queue = [];
|
|
level.identified_entities = [];
|
|
|
|
level.radio_tower_goal_nodes = GetNodeArray( "radio tower goal", "script_noteworthy" );
|
|
level.fuel_depot_goal_nodes = GetNodeArray( "fuel depot goal", "script_noteworthy" );
|
|
|
|
level.custom_target = undefined;
|
|
|
|
level.vehicle_death_thread = [];
|
|
level.vehicle_death_thread["see2_panzeriv"] = maps\see2_vehicle_behavior::see2_veh_death_thread;
|
|
level.vehicle_death_thread["see2_tiger"] = maps\see2_vehicle_behavior::see2_veh_death_thread;
|
|
level.vehicle_death_thread["see2_panther"] = maps\see2_vehicle_behavior::see2_veh_death_thread;
|
|
|
|
level.finished_events = [];
|
|
thread do_friendly_consistency_check();
|
|
thread do_radio_tower_explode();
|
|
level thread wait_for_finalbattle_alternate(); //-- Glocke: to fit with the new objective text
|
|
|
|
level.centroid = undefined;
|
|
level thread keep_track_of_player_centroid();
|
|
level thread wait_for_dialog_triggers();
|
|
level thread see2_handleLineQueue();
|
|
|
|
level.retreat_points = [];
|
|
level.retreat_points = array_add( level.retreat_points, "radio tower" );
|
|
level.retreat_points = array_add( level.retreat_points, "fuel depot" );
|
|
level.retreat_points = array_add( level.retreat_points, "airstrike" );
|
|
|
|
level.invalid_retreat_points = [];
|
|
|
|
level.time_between_dialogue = 9;
|
|
level.dialogue_timer = 0;
|
|
level.min_close_infantry_for_warning = 3;
|
|
level.min_distance_for_close_infantry = 120;
|
|
|
|
// Idle warning variables
|
|
level.current_idle_time = 0;
|
|
level.min_idle_dist_sq = 0.5;
|
|
level.idle_warn_time = 10;
|
|
level.num_idle_lines = 6;
|
|
|
|
|
|
level thread update_dialogue_timer();
|
|
level thread setup_achievement_spawn_func();
|
|
level thread spawn_func_throttle_spawns();
|
|
|
|
level.callbackSaveRestored = ::see2_callback_saveRestored;
|
|
|
|
/#
|
|
level thread debug_ai_prints();
|
|
#/
|
|
|
|
BadPlacesEnable( 0 );
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// GLOBAL UTILITY FUNCTIONS
|
|
////////////////////////////////////////
|
|
|
|
tune_max_ai_numbers()
|
|
{
|
|
level.max_ai_spawn = 20;
|
|
level.max_vehicles = 8;
|
|
|
|
level.max_ai_spawn_current = 20;
|
|
level.max_vehicle_spawn_current = 8;
|
|
|
|
//TODO: FINISH THIS LATER WHEN I KNOW WHAT I'M DOING WITH IT
|
|
if(1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
current_number_of_vehicles = [];
|
|
current_number_of_vehicles = GetEntArray( "script_vehicle", "classname" );
|
|
|
|
for( i=0; i < current_number_of_vehicles.size; i++)
|
|
{
|
|
|
|
}
|
|
|
|
wait(1);
|
|
}
|
|
}
|
|
|
|
// AI targeting tune values
|
|
tune_ai()
|
|
{
|
|
level.number_miss_structs = 8;
|
|
level.min_miss_distance = 150;
|
|
level.max_miss_distance = 250;
|
|
|
|
//Distances used for tank targeting
|
|
level.see2_max_tank_target_dist = 4000;
|
|
level.see2_max_tank_firing_dist = 3500;
|
|
|
|
level.retreat_threshold = 0.6;
|
|
|
|
min_pzrsk_dist = 500;
|
|
max_pzrsk_dist = 2500;
|
|
level.min_panzerschreck_eng_distsq = min_pzrsk_dist*min_pzrsk_dist;
|
|
level.max_panzerschreck_eng_distsq = max_pzrsk_dist*max_pzrsk_dist;
|
|
}
|
|
|
|
// --- DIFFICULTY SCALING ---
|
|
difficulty_scale()
|
|
{
|
|
switch( GetDifficulty() )
|
|
{
|
|
case "easy":
|
|
level.see2_player_armor = 7500;
|
|
level.see2_base_lag_time = 1;
|
|
level.see2_max_targeters = 3;
|
|
level.see2_percent_life_at_checkpoint = 1;
|
|
level.time_before_regen = 9;
|
|
level.no_shoot_increase = .05;
|
|
break;
|
|
|
|
case "medium":
|
|
level.see2_player_armor = 6000;
|
|
level.see2_base_lag_time = 0.75;
|
|
level.see2_max_targeters = 3;
|
|
level.see2_percent_life_at_checkpoint = 0.75;
|
|
level.time_before_regen = 10;
|
|
level.no_shoot_increase = .05;
|
|
break;
|
|
|
|
case "hard":
|
|
level.see2_player_armor = 4500;
|
|
level.see2_base_lag_time = 0.5;
|
|
level.see2_max_targeters = 4;
|
|
level.see2_percent_life_at_checkpoint = 0.5;
|
|
level.time_before_regen = 11;
|
|
level.no_shoot_increase = .03;
|
|
break;
|
|
|
|
case "fu":
|
|
level.see2_player_armor = 3000;
|
|
level.see2_base_lag_time = 0.25;
|
|
level.see2_max_targeters = 4;
|
|
level.see2_percent_life_at_checkpoint = 0.5;
|
|
level.time_before_regen = 12;
|
|
level.no_shoot_increase = .01;
|
|
break;
|
|
}
|
|
|
|
level.armor_per_frame = int( level.see2_player_armor / level.time_before_regen / 20 );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: keep_track_of_achievement
|
|
// CALLED ON: level
|
|
// PURPOSE: Keeps track of the status of the achievement and then awards it to all players when it has been accomplished
|
|
//
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
keep_track_of_achievement()
|
|
{
|
|
//-- because the water towers weren't hooked to anything
|
|
water_tower_trigs = GetEntArray( "water_tower_trig", "targetname" );
|
|
for(i = 0; i < water_tower_trigs.size; i++)
|
|
{
|
|
water_tower_trigs[i] thread track_water_tower_triggers();
|
|
}
|
|
|
|
//-- The Max that are in the level
|
|
water_tower_number = 3;
|
|
bunker_number = 5;
|
|
schreck_number = 14;
|
|
|
|
//-- The counters
|
|
water_tower_achievement = 0;
|
|
bunker_achievement = 0;
|
|
schreck_tower_achievement = 0;
|
|
|
|
|
|
while(1)
|
|
{
|
|
level waittill( "achievement_destroy_notify", item );
|
|
|
|
switch( item )
|
|
{
|
|
case "water_tower":
|
|
water_tower_achievement++;
|
|
break;
|
|
|
|
case "bunker":
|
|
bunker_achievement++;
|
|
break;
|
|
|
|
case "schreck_tower":
|
|
schreck_tower_achievement++;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//-- Check and give award
|
|
if( water_tower_achievement >= water_tower_number && bunker_achievement >= bunker_number && schreck_tower_achievement >= schreck_number )
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
players = get_players();
|
|
|
|
for(i=0; i < players.size; i++)
|
|
{
|
|
players[i] maps\_utility::giveachievement_wrapper( "SEE2_ACHIEVEMENT_TOWER" );
|
|
}
|
|
|
|
}
|
|
|
|
track_water_tower_triggers()
|
|
{
|
|
self waittill( "trigger" );
|
|
level notify( "achievement_destroy_notify", "water_tower" );
|
|
}
|
|
|
|
setup_achievement_spawn_func()
|
|
{
|
|
}
|
|
|
|
spawn_func_throttle_spawns()
|
|
{
|
|
ai_array = GetSpawnerArray();
|
|
|
|
for( i=0; i<ai_array.size; i++)
|
|
{
|
|
//ai_array[i] add_spawn_function( ::throttle_ai_keep_time ); //called by throttle_ai_make_room
|
|
ai_array[i] add_spawn_function( ::throttle_ai_make_room );
|
|
}
|
|
}
|
|
|
|
throttle_ai_keep_time()
|
|
{
|
|
self endon("death");
|
|
|
|
while(true)
|
|
{
|
|
self.see2_life_time += 0.001;
|
|
wait(0.1);
|
|
}
|
|
}
|
|
|
|
throttle_ai_make_room()
|
|
{
|
|
ai = GetAIArray( "axis" );
|
|
|
|
//-- ai setup
|
|
|
|
for(i = 0; i < ai.size; i++)
|
|
{
|
|
if(!IsDefined(ai[i].see2_life_time))
|
|
{
|
|
ai[i].see2_life_time = 0;
|
|
ai[i] thread throttle_ai_keep_time();
|
|
}
|
|
}
|
|
|
|
if( ai.size > level.max_ai_spawn_current )
|
|
{
|
|
ai = sort_ai_by_life_time( ai );
|
|
num_ai_to_remove = ai.size - level.max_ai_spawn_current;
|
|
|
|
for( i=0; i < num_ai_to_remove; i++ )
|
|
{
|
|
//ai[i] Delete(); //-- change this to bloody death
|
|
random_wait = RandomFloatRange(0.1, 1.0);
|
|
ai[i] thread bloody_death( random_wait );
|
|
}
|
|
}
|
|
}
|
|
|
|
sort_ai_by_life_time( nodes )
|
|
{
|
|
for( i = 0; i < nodes.size; i++ )
|
|
{
|
|
for( j = i; j < nodes.size; j++ )
|
|
{
|
|
if( nodes[j].see2_life_time > nodes[i].see2_life_time )
|
|
{
|
|
temp = nodes[i];
|
|
nodes[i] = nodes[j];
|
|
nodes[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: keep_track_of_player_centroid
|
|
// CALLED ON: level
|
|
// PURPOSE: Keeps track of the centroid of all players for use by anyone who might want to stay
|
|
// as close to the players as a whole as possible
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
keep_track_of_player_centroid()
|
|
{
|
|
wait_for_first_player();
|
|
while( 1 )
|
|
{
|
|
players = get_players();
|
|
num_players = players.size;
|
|
total = undefined;
|
|
for( i = 0; i < num_players; i++ )
|
|
{
|
|
if( !isDefined( total ) )
|
|
{
|
|
total = players[i].origin;
|
|
}
|
|
else
|
|
{
|
|
total = total + players[i].origin;
|
|
}
|
|
}
|
|
level.centroid = total/num_players;
|
|
wait( 3 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: keep_grenade_bags_from_spawning
|
|
// CALLED ON: level
|
|
// PURPOSE: Keeps grenade bags from spawning and saves us some entities
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
keep_grenade_bags_from_spawning()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
level.nextGrenadeDrop = 9999;
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_flak88_guard
|
|
// CALLED ON: a guard for flak88s
|
|
// PURPOSE: Deprecated in favor of drones... I think
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_flak88_guard()
|
|
{
|
|
self.ignoreall = true;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_group_spawn
|
|
// CALLED ON: level
|
|
// PURPOSE: Waits until a predefined signal is sent then spawns a specific group
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_group_spawn( group )
|
|
{
|
|
trigger = GetEnt( "group "+group+" spawn", "script_noteworthy" );
|
|
trigger thread inform_on_touch_trigger( trigger.script_noteworthy );
|
|
level waittill( trigger.script_noteworthy );
|
|
|
|
if( flag( level.skipto_flag_names[group] ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
do_area_spawn( group );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_friendly_consistency_check
|
|
// CALLED ON: level
|
|
// PURPOSE: This iterates through all friendly paths and makes sure all are sound in terms of
|
|
// naming conventions
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_friendly_consistency_check()
|
|
{
|
|
/#
|
|
names = [];
|
|
names[0] = "friendly 1";
|
|
names[1] = "friendly 2";
|
|
names[2] = "friendly 3";
|
|
|
|
advance_triggers = GetEntArray( "friendly advance trigger", "script_noteworthy" );
|
|
|
|
|
|
for( i = 0; i < advance_triggers.size; i++ )
|
|
{
|
|
advance_nodes = GetVehicleNodeArray( advance_triggers[i].script_noteworthy, "script_noteworthy" );
|
|
if( advance_nodes.size != 3 )
|
|
{
|
|
iprintlnbold( "Insufficient advance nodes for trigger "+advance_triggers[i].script_noteworthy );
|
|
}
|
|
for( j = 0; j < names.size; j++ )
|
|
{
|
|
found = false;
|
|
for( k = 0; k < advance_nodes.size; k++ )
|
|
{
|
|
if( advance_nodes[k].script_string == names[j] )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
iprintlnbold( "Advance node "+advance_triggers[i].script_noteworthy+" does not exist for ent "+names[j] );
|
|
}
|
|
}
|
|
}
|
|
#/
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: see2_callback_saveRestored
|
|
// CALLED ON: ?
|
|
// PURPOSE: This is threaded when the level reloads from a checkpoint. It handles armor fixup
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
see2_callback_saveRestored()
|
|
{
|
|
maps\_callbackglobal::Callback_SaveRestored();
|
|
|
|
for( i = 0; i < get_players().size; i++ )
|
|
{
|
|
if( (get_players()[i].myTank.armor / get_players()[i].myTank.maxarmor) < 0.5 )
|
|
{
|
|
get_players()[i].myTank.armor = get_players()[i].myTank.maxarmor * level.see2_percent_life_at_checkpoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: explosive_damage
|
|
// CALLED ON: anything
|
|
// PURPOSE: checks to see if a damage type is considered explosive for purposes of destruction
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
explosive_damage( type )
|
|
{
|
|
return (issubstr( type, "GRENADE" ) || issubstr( type, "PROJECTILE" ) || issubstr( type, "EXPLOSIVE" ) || issubstr( type, "BURNED" ) );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: inform_on_touch_trigger
|
|
// CALLED ON: a trigger
|
|
// PURPOSE: This sends a signal to the level when the trigger is breached, commented section checked
|
|
// that the entity triggering was a player. I don't know why this was disabled. It may be
|
|
// why the airstrike is triggering early
|
|
// ADDITIONS NEEDED: Look and see where this is being used and see why it is not checking for players
|
|
/////////////////////////////////
|
|
inform_on_touch_trigger( event )
|
|
{
|
|
self waittill( "trigger" );
|
|
level notify( event );
|
|
|
|
/*
|
|
breaker = false;
|
|
while( !breaker )
|
|
{
|
|
for( i = 0; i < get_players().size; i++ )
|
|
{
|
|
if( get_players()[i] isTouching( self ) )
|
|
{
|
|
self notify( "trigger" );
|
|
breaker = true;
|
|
break;
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}*/
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: inform_on_damage_trigger
|
|
// CALLED ON: a damage trigger
|
|
// PURPOSE: This sends a notify to the level if a player or friendly tank does explosive damage to
|
|
// a trigger
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
inform_on_damage_trigger( event )
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "damage", damage, other, direction, origin, damage_type );
|
|
if ( explosive_damage( damage_type ) )
|
|
{
|
|
if( isDefined( other.script_team ) && other.script_team == "allies" )
|
|
{
|
|
level notify( event );
|
|
return;
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: is_in_player_arc
|
|
// CALLED ON: an object, with a player as an argument
|
|
// PURPOSE: This checks to see if the object is in the view arc of the player passed in
|
|
// ADDITIONS NEEDED: Maybe have it adjust view arc properly for widescreen FOV
|
|
/////////////////////////////////
|
|
is_in_player_arc( player )
|
|
{
|
|
myOrigin = (self.origin[0], self.origin[1], 0);
|
|
playerOrigin = (player.origin[0], player.origin[1], 0);
|
|
diff = myOrigin - playerOrigin;
|
|
angles = vectortoangles( diff );
|
|
if( abs( angles[1] - player.angles[1] ) > 32.5 )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_level
|
|
// CALLED ON: level
|
|
// PURPOSE: Does all threat bias setup
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_level()
|
|
{
|
|
CreateThreatBiasGroup( "players" );
|
|
create_see2_threat_group( "players" );
|
|
CreateThreatBiasGroup( "player tanks" );
|
|
create_see2_threat_group( "player tanks" );
|
|
CreateThreatBiasGroup( "enemy antitank" );
|
|
create_see2_threat_group( "enemy antitank" );
|
|
CreateThreatBiasGroup( "enemy infantry" );
|
|
create_see2_threat_group( "enemy infantry" );
|
|
CreateThreatBiasGroup( "friendly antitank" );
|
|
create_see2_threat_group( "friendly antitank" );
|
|
CreateThreatBiasGroup( "friendly infantry" );
|
|
create_see2_threat_group( "friendly infantry" );
|
|
CreateThreatBiasGroup( "friendly armor" );
|
|
create_see2_threat_group( "friendly armor" );
|
|
create_see2_threat_group( "enemy armor" );
|
|
|
|
//SetThreatBias( "players", "enemy antitank", 10000 );
|
|
SetThreatBias( "player tanks", "enemy antitank", 10000 );
|
|
SetThreatBias( "friendly armor", "enemy antitank", 15000 );
|
|
SetIgnoreMeGroup( "players", "enemy infantry" );
|
|
SetIgnoreMeGroup( "players", "enemy antitank" );
|
|
SetIgnoreMeGroup( "friendly armor", "enemy infantry" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_player_tanks
|
|
// CALLED ON: level
|
|
// PURPOSE: Loads all players into their tanks, keeps them from exiting. It replaces any unused
|
|
// player tanks with friendly AI tanks and threads all the friendly vehicle AI stuff on
|
|
// them.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_player_tanks()
|
|
{
|
|
entry_points = [];
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
entry_points = array_add( entry_points, getstruct( "orig_enter_tanks"+(i+1), "targetname" ) );
|
|
}
|
|
|
|
for( i = 0; i < entry_points.size; i++ )
|
|
{
|
|
tank = getent( entry_points[i].target, "targetname" );
|
|
if( isDefined( get_players()[i] ) )
|
|
{
|
|
tank.animname = "ot34";
|
|
|
|
if( i == 2 )
|
|
{
|
|
flag_set( "coop" );
|
|
}
|
|
|
|
// Set the players' origin to the entry point because useby() is based on distance
|
|
//players[i] setOrigin( entry_points[i].origin );
|
|
get_players()[i] setOrigin( tank gettagorigin( "tag_enter_driver" ) );
|
|
get_players()[i] setThreatBiasGroup( "players" );
|
|
tank makevehicleunusable();
|
|
/#
|
|
tank makevehicleusable();
|
|
#/
|
|
|
|
tank useby( get_players()[i] );
|
|
get_players()[i].mytank = tank;
|
|
//get_players()[i] thread check_for_flamethrower();
|
|
tank thread update_current_targeters();
|
|
|
|
delta = maps\see2_breadcrumbs::add_new_breadcrumb_ent( tank, 10, 200 );
|
|
if( isDefined( delta ) )
|
|
{
|
|
tank.my_targeting_delta = delta;
|
|
}
|
|
|
|
tank thread vehicle_damage();
|
|
tank thread disconnect_paths_around_me_based_on_speed();
|
|
//tank thread triggered_drop_bad_areas();
|
|
//tank thread constant_drop_bad_place();
|
|
tank thread friendly_fire_kill_tank();
|
|
// god mode for tanks
|
|
tank thread keep_tank_alive();
|
|
}
|
|
else
|
|
{
|
|
level.player_tanks = array_remove( level.player_tanks, tank );
|
|
old_script_int = tank.script_int;
|
|
tank delete();
|
|
spawn_trigger = getEnt( "friendly "+i+" spawn_trigger", "script_noteworthy" );
|
|
move_trigger = getEnt( "friendly "+i+" move_trigger", "script_noteworthy" );
|
|
spawn_trigger notify( "trigger" );
|
|
wait( 0.05 );
|
|
move_trigger notify( "trigger" );
|
|
wait( 0.05 );
|
|
new_tank = getEnt( "friendly "+i, "script_noteworthy" );
|
|
level.player_tanks = array_add( level.player_tanks, new_tank );
|
|
new_tank.script_int = i;
|
|
new_tank setSpeed( 0, 1000, 1000 );
|
|
new_tank thread do_player_support();
|
|
new_tank thread cleanup_targeting();
|
|
new_tank maps\_vehicle::mgoff();
|
|
new_tank.current_node = getVehicleNode( "friendly "+i+" start", "script_noteworthy" );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-- Whenever a set of AI wants to retreat and try to pick a new goal node, they will
|
|
// cause the player tanks to drop bad areas, so that the AI will try and avoid them a little smarter.
|
|
|
|
triggered_drop_bad_areas()
|
|
{
|
|
self endon("death"); //-- shouldn't ever happen with a players tank...
|
|
|
|
while(1)
|
|
{
|
|
level waittill("ai_set_new_retreat_node");
|
|
|
|
self_forward = AnglesToForward(self.angles);
|
|
self_right = AnglesToRight(self.angles);
|
|
|
|
BadPlace_Cylinder( "center", 10, self.origin, 64, 100, "axis" );
|
|
BadPlace_Cylinder( "front_left", 10, self.origin + (self_forward * 62) - (self_right * 62), 64, 100, "axis" );
|
|
BadPlace_Cylinder( "front_right", 10, self.origin + (self_forward * 62) + (self_right * 62), 64, 100, "axis" );
|
|
BadPlace_Cylinder( "rear_left", 10, self.origin - (self_forward * 62) - (self_right * 62), 64, 100, "axis" );
|
|
BadPlace_Cylinder( "rear_right", 10, self.origin - (self_forward * 62) + (self_right * 62), 64, 100, "axis" );
|
|
|
|
wait(5);
|
|
}
|
|
|
|
}
|
|
|
|
constant_drop_bad_place()
|
|
{
|
|
lifetime = 2;
|
|
old_position = self.origin;
|
|
|
|
while(1)
|
|
{
|
|
self_forward = AnglesToForward(self.angles);
|
|
self_right = AnglesToRight(self.angles);
|
|
|
|
if( distanceSquared(self.origin, old_position) > 8*8 )
|
|
{
|
|
BadPlace_Cylinder( "center", lifetime, self.origin, 200, 100, "axis" );
|
|
//BadPlace_Cylinder( "front_left", lifetime, self.origin + (self_forward * 62) - (self_right * 62), 64, 100, "axis" );
|
|
//BadPlace_Cylinder( "front_right", lifetime, self.origin + (self_forward * 62) + (self_right * 62), 64, 100, "axis" );
|
|
//BadPlace_Cylinder( "rear_left", lifetime, self.origin - (self_forward * 62) - (self_right * 62), 64, 100, "axis" );
|
|
//BadPlace_Cylinder( "rear_right", lifetime, self.origin - (self_forward * 62) + (self_right * 62), 64, 100, "axis" );
|
|
old_position = self.origin;
|
|
wait(1);
|
|
}
|
|
else
|
|
{
|
|
wait(0.1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-- This is done on the player's tank to try and keep AI from running through him.
|
|
//-- going to try and do this based on speed.
|
|
|
|
disconnect_paths_around_me_based_on_speed()
|
|
{
|
|
self endon("death"); //-- this will probably never get called
|
|
|
|
while(1)
|
|
{
|
|
while( self GetSpeed() > 1 )
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
self DisconnectPaths();
|
|
|
|
while(self GetSpeed() < 1)
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
self ConnectPaths();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_delete_triggers
|
|
// CALLED ON: level
|
|
// PURPOSE: Threads delete_trigger behavior on all triggers threaded as such.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_delete_triggers()
|
|
{
|
|
delete_triggers = getEntArray( "trigger_delete", "script_noteworthy" );
|
|
for( i = 0; i < delete_triggers.size; i++ )
|
|
{
|
|
delete_triggers[i] thread do_delete_trigger();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_delete_trigger
|
|
// CALLED ON: a trigger whose script_noteworthy is trigger_delete
|
|
// PURPOSE: Deletes any entities that contact the trigger
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_delete_trigger()
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", guy );
|
|
guy delete();
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_early_tanks
|
|
// CALLED ON: level
|
|
// PURPOSE: sets up all flame bunker fortifications, makes player tanks invulnerable, threads a
|
|
// bunch of basic level functions. Is a poorly named function :(
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_early_tanks()
|
|
{
|
|
flak88_guard_array = GetEntArray( "flak 88 guards", "script_noteworthy" );
|
|
array_thread(flak88_guard_array, ::add_spawn_function, ::setup_flak88_guard);
|
|
|
|
flame_bunker_array = GetEntArray( "flame bunker one guard", "script_noteworthy" );
|
|
flame_bunker_array = array_combine( flame_bunker_array, GetEntArray( "flame bunker two guard", "script_noteworthy" ) );
|
|
flame_bunker_array = array_combine( flame_bunker_array, GetEntArray( "flame bunker three guard", "script_noteworthy" ) );
|
|
array_thread( flame_bunker_array, ::add_spawn_function, ::setup_flame_bunker_guard);
|
|
|
|
//level thread setup_spawngroup_generics( 0 );
|
|
|
|
/*
|
|
vehicle = getent("the_gun","targetname");
|
|
|
|
// here's the magic, the arty move function
|
|
vehicle do_arty_move( "initial_arty_node" );
|
|
*/
|
|
|
|
|
|
/*
|
|
level.friendly_tanks[0] = getent( "t34_1", "targetname" );
|
|
level.friendly_tanks[1] = getent( "t34_2", "targetname" );
|
|
level.friendly_tanks[2] = getent( "t34_3", "targetname" );
|
|
|
|
level.friendly_tanks[0] thread keep_tank_alive();
|
|
level.friendly_tanks[1] thread keep_tank_alive();
|
|
level.friendly_tanks[2] thread keep_tank_alive();
|
|
*/
|
|
|
|
thread do_flame_bunker( "flame bunker one", "bunker one flamed" , true, true, false, true );
|
|
thread do_flame_bunker( "flame bunker two", "bunker two flamed" , true, true, true, true );
|
|
thread do_flame_bunker( "flame bunker three", "bunker three flamed", true );
|
|
thread do_flame_bunker( "flame bunker four", "bunker four flamed" , true, true, true );
|
|
thread do_flame_bunker( "flame bunker six", "bunker six flamed" , true, true, true );
|
|
thread keep_grenade_bags_from_spawning();
|
|
//thread wait_for_guard_tower_sploders(); // Glocke: replaced by do_fortification_spawn
|
|
thread do_fake_schrecks();
|
|
|
|
level.player_tanks = [];
|
|
for(i = 0; i < 4; i++ )
|
|
{
|
|
ent = getEnt( "player_tank_"+(i+1), "targetname" );
|
|
if( isDefined( ent ) )
|
|
{
|
|
level.player_tanks = array_add( level.player_tanks, ent );
|
|
}
|
|
}
|
|
|
|
// TEMP put in so players don't die in tanks
|
|
for( i = 0; i < level.player_tanks.size; i++ )
|
|
{
|
|
if( isDefined( level.player_tanks[i] ) )
|
|
{
|
|
level.player_tanks[i] thread keep_tank_alive();
|
|
}
|
|
}
|
|
level thread setup_friendly_advance_triggers();
|
|
/#
|
|
//level thread draw_ent_locations( level.friendly_tanks, "*TANK*", "stop_draw_tank_locations" );
|
|
#/
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_area_spawn
|
|
// CALLED ON: level
|
|
// PURPOSE: waits on any area spawn triggers that exist for listed areaNum. Sends a signal that
|
|
// will start the area spawn when the trigger is activated
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_area_spawn( areaNum )
|
|
{
|
|
trigger_array = getEntArray( "area"+areaNum+" spawn trigger", "targetname" );
|
|
for( i = 0; i < trigger_array.size; i++ )
|
|
{
|
|
trigger_array[i] notify( "trigger" );
|
|
wait_network_frame();
|
|
}
|
|
|
|
thread setup_spawngroup_generics( areaNum ); // Vehicle Spawns
|
|
thread do_fortification_spawn( areaNum ); // Drone Spawns
|
|
|
|
/*
|
|
if( areaNum == 3 )
|
|
{
|
|
level thread do_tiger_retreats();
|
|
}
|
|
*/
|
|
level notify( "area"+areaNum+" spawned" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: log_finished_event
|
|
// CALLED ON: level
|
|
// PURPOSE: This keeps track of events for use by other systems that may be waiting on an event.
|
|
// This should probably be replaced with two or three flags in the subsystems still using
|
|
// it.
|
|
// ADDITIONS NEEDED: Should be deprecated at some point
|
|
/////////////////////////////////
|
|
Log_Finished_Event( event, waittime )
|
|
{
|
|
if( isDefined( waittime ) )
|
|
{
|
|
wait( waittime );
|
|
}
|
|
if( array_check_for_dupes( level.finished_events, event ) )
|
|
{
|
|
level.finished_events = array_add( level.finished_events, event );
|
|
level notify( event );
|
|
}
|
|
}
|
|
|
|
////////////////////////////
|
|
// PLAYER DAMAGE AND HUD
|
|
////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: begin_armor_regen
|
|
// CALLED ON: player vehicle
|
|
// PURPOSE: This begins regenerating the player armor until the player is damaged or dies
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
begin_armor_regen()
|
|
{
|
|
self endon( "damage" );
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
if( self.armor > self.maxarmor )
|
|
{
|
|
self.armor = self.maxarmor;
|
|
}
|
|
if( self.armor == self.maxarmor )
|
|
{
|
|
break;
|
|
}
|
|
|
|
self.armor += level.armor_per_frame;
|
|
wait( 0.05 );
|
|
}
|
|
self notify( "armor_full" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_flamethrower
|
|
// CALLED ON: a player
|
|
// PURPOSE: This causes the hull mounted flamethrower or mg to mirror the player's turret
|
|
// direction, allowing aiming
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_for_flamethrower()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
self.myTank endon( "death" );
|
|
self.mytank thread do_my_loop();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_my_loop
|
|
// CALLED ON: player tank
|
|
// PURPOSE: actually does the aiming of the hull gun
|
|
// ADDITIONS NEEDED: none
|
|
/////////////////////////////////
|
|
do_my_loop()
|
|
{
|
|
self endon( "stop firing" );
|
|
|
|
while( 1 )
|
|
{
|
|
angles = self getTagAngles( "tag_barrel" );
|
|
|
|
angles = (angles[0]-5, angles[1], angles[2] );
|
|
|
|
forward = anglesToForward( angles );
|
|
|
|
target_vec = self getTagOrigin( "tag_gunner_barrel1" ) + (forward*50);
|
|
|
|
self setGunnerTargetVec( target_vec, 0 );
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: vehicle_damage
|
|
// CALLED ON: player vehicle
|
|
// PURPOSE: This does the scripted damage system for the tank. It keeps track of an armor value
|
|
// which is decreased by enemy attacks and also sets up the placeholder vehicle damage
|
|
// hud elements. It then threads the functions that will keep track of and act on these
|
|
// values.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
vehicle_damage()
|
|
{
|
|
self endon( "death" );
|
|
players = get_players();
|
|
vehicle_owner = self getvehicleowner();
|
|
vehicle_owner endon( "death" );
|
|
vehicle_owner endon( "disconnect" );
|
|
|
|
myPlayer = undefined;
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
if( players[i] == vehicle_owner )
|
|
{
|
|
myPlayer = players[i];
|
|
}
|
|
}
|
|
if( !isDefined( myPlayer ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self.armor = level.see2_player_armor;
|
|
self.maxarmor = level.see2_player_armor;
|
|
//self.armorhudelem = newClientHudElem(myPlayer);
|
|
//self.armorhudback = newClientHudElem(myPlayer);
|
|
//self.armorhudelem.sort = 2;
|
|
//self.armorhudelem.x = 111;
|
|
//self.maxhudy = 72;
|
|
//self.armorhudelem.y = 380;
|
|
self.starty = 380;
|
|
//self.armorhudback.sort = 1;
|
|
//self.armorhudback.x = 110;
|
|
//self.armorhudback.y = 379;
|
|
//self.armorhudback setshader( "black", 11, 74 );
|
|
//self.armorhudelem setshader( "white", 8, 72 );
|
|
|
|
self thread do_veh_damage_hud();
|
|
self thread do_veh_extra_damage_no_play();
|
|
|
|
while( 1 )
|
|
{
|
|
|
|
self waittill( "damage", amount, attacker );
|
|
|
|
|
|
if( isGodMode( myPlayer ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// ignore mortar hits TODO check this for other cases
|
|
if( !array_check_for_dupes( get_players(), attacker ) || !array_check_for_dupes( level.player_tanks, attacker ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( self.armor > 0 )
|
|
{
|
|
dmg_faked = amount * self.do_extra_dmg_no_fire; //-- modifier if player isn't firing main turret
|
|
|
|
if( IsDefined( self.too_close_damage_modifier) )
|
|
{
|
|
dmg_faked = dmg_faked * self.too_close_damage_modifier; //-- modifier if player is humping an AI tank
|
|
}
|
|
|
|
self.armor -= dmg_faked;
|
|
}
|
|
else
|
|
{
|
|
self kill_tank();
|
|
}
|
|
|
|
if( isdefined( myPlayer ) )
|
|
{
|
|
hurtTime = gettime();
|
|
level.hurtTime = hurtTime;
|
|
|
|
if( amount < 500 )
|
|
{
|
|
myPlayer PlayRumbleOnEntity( "tank_damage_light_mp" );
|
|
}
|
|
else
|
|
{
|
|
myPlayer PlayRumbleOnEntity( "tank_damage_heavy_mp" );
|
|
}
|
|
|
|
myPlayer viewkick( 127, attacker.origin );
|
|
myPlayer player_flag_set( "player_has_red_flashing_overlay" );
|
|
myPlayer startfadingblur( 3, 2 );
|
|
|
|
playerJustGotRedFlashing = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-- This monitors whether or not the player has fired their turret or not, if they haven't then
|
|
// the damage starts to scale up on the enemy tanks
|
|
|
|
do_veh_extra_damage_no_play()
|
|
{
|
|
self endon("death");
|
|
|
|
if(!IsDefined( self.do_extra_dmg_no_fire ) )
|
|
{
|
|
self.do_extra_dmg_no_fire = 1;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
self thread do_veh_extra_damage_no_play_update();
|
|
self waittill("weapon_fired");
|
|
self.do_extra_dmg_no_fire = 1;
|
|
wait(0.05);
|
|
}
|
|
|
|
}
|
|
|
|
do_veh_extra_damage_no_play_update()
|
|
{
|
|
self endon("weapon_fired");
|
|
wait(60);
|
|
|
|
while(1)
|
|
{
|
|
if(self.do_extra_dmg_no_fire < 3)
|
|
{
|
|
self.do_extra_dmg_no_fire += level.no_shoot_increase;
|
|
wait(5);
|
|
}
|
|
else
|
|
{
|
|
wait(10); //-- kind of arbitrary
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: kill_tank
|
|
// CALLED ON: player tank
|
|
// PURPOSE: This does the vehicle model swap, plays death fx and locks player controls. This is
|
|
// because the player or tank dying does terrible things to the engine last I checked.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
kill_tank()
|
|
{
|
|
if(get_players().size > 1)
|
|
{
|
|
self see2_fail_the_mission_coop();
|
|
return;
|
|
}
|
|
|
|
self setModel( "vehicle_rus_tracked_ot34_d" );
|
|
playfx( level._effect["tank_destruct"], self.origin );
|
|
player = self getVehicleOwner();
|
|
player freezeControls( true );
|
|
wait( 1 );
|
|
maps\_utility::missionFailedWrapper();
|
|
}
|
|
|
|
|
|
see2_fail_the_mission_coop()
|
|
{
|
|
self setModel( "vehicle_rus_tracked_ot34_d" );
|
|
playfx( level._effect["tank_destruct"], self.origin );
|
|
player = self getVehicleOwner();
|
|
player freezeControls( true );
|
|
|
|
players = get_players();
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
if( isDefined( players[i] ) )
|
|
{
|
|
players[i] thread maps\_quotes::displayMissionFailed();
|
|
if( players[i] == player )
|
|
{
|
|
players[i] thread maps\_quotes::displayPlayerDead();
|
|
println( "Player #"+i+" is dead" );
|
|
}
|
|
else
|
|
{
|
|
players[i] thread maps\_quotes::displayTeammateDead( player );
|
|
println( "Player #"+i+" is alive" );
|
|
}
|
|
}
|
|
}
|
|
|
|
wait( 1 );
|
|
maps\_utility::missionFailedWrapper();
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: friendly_fire_kill_tank
|
|
// CALLED ON: player tank
|
|
// PURPOSE: This will trigger a mission fail if a player repeatedly shoots a friendly AI tank
|
|
//
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
|
|
friendly_fire_kill_tank()
|
|
{
|
|
player = self getVehicleOwner();
|
|
|
|
if(!isPlayer(player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
player.friendlyfire_count = 0;
|
|
player thread friendly_fire_kill_tank_reduce();
|
|
|
|
while(1)
|
|
{
|
|
level waittill( "friendly attacked by player", attacker );
|
|
|
|
if( player == attacker )
|
|
{
|
|
player.friendlyfire_count++;
|
|
}
|
|
|
|
if(player.friendlyfire_count >= 3)
|
|
{
|
|
maps\_friendlyfire::missionfail();
|
|
}
|
|
}
|
|
}
|
|
|
|
friendly_fire_kill_tank_reduce()
|
|
{
|
|
self endon("death");
|
|
|
|
while(1)
|
|
{
|
|
while(self.friendlyfire_count == 0)
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
wait(10);
|
|
self.friendlyfire_count--;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: cleanup_damage_hud
|
|
// CALLED ON: player
|
|
// PURPOSE: This gets rid of the damage hud for cutscenes, etc.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
cleanup_damage_hud()
|
|
{
|
|
self waittill( "stop damage hud" );
|
|
|
|
//self.myTank.armorhudelem.alpha = 0;
|
|
//self.myTank.armorhudback.alpha = 0;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_veh_damage_hud
|
|
// CALLED ON: player vehicle
|
|
// PURPOSE: This shows the armor bar (which is temp) and decreases it appropriately. This will
|
|
// eventually be replaced by coloring the tank icon next to the compass red to show
|
|
// damage.
|
|
// ADDITIONS NEEDED: Will eventually be deprecated in favor of coloring tank icon through code
|
|
/////////////////////////////////
|
|
do_veh_damage_hud()
|
|
{
|
|
self endon( "death" );
|
|
|
|
vehicle_owner = self getvehicleowner();
|
|
vehicle_owner endon( "death" );
|
|
vehicle_owner endon( "disconnect" );
|
|
vehicle_owner endon( "stop damage hud" );
|
|
vehicle_owner thread cleanup_damage_hud();
|
|
|
|
slow_flash_time = 1;
|
|
slow_flash_color = (1, 1, 0);
|
|
fast_flash_time = 0.5;
|
|
fast_flash_color = (1, 0, 0);
|
|
|
|
self thread regen_armor();
|
|
|
|
current_time = 0;
|
|
while( 1 )
|
|
{
|
|
percent = self.armor/self.maxarmor;
|
|
if( percent < 0 )
|
|
{
|
|
percent = 0;
|
|
}
|
|
//current_y = int( self.maxhudy * percent );
|
|
//if( current_y <= 0 )
|
|
//{
|
|
//current_y = 1;
|
|
//}
|
|
//self.armorhudelem setshader( "white", 8, current_y );
|
|
//self.armorhudelem.y = self.starty + (self.maxhudy-current_y);
|
|
|
|
if( percent > 0.1 && percent <= 0.25 )
|
|
{
|
|
if( current_time >= slow_flash_time )
|
|
{
|
|
current_time = 0;
|
|
}
|
|
percent_flash = current_time/slow_flash_time;
|
|
//self.armorhudelem.color = ( (1-percent_flash)*(1,1,1) ) + (percent_flash*slow_flash_color);
|
|
current_time += 0.05;
|
|
}
|
|
else if( percent < 0.1 )
|
|
{
|
|
if( current_time >= fast_flash_time )
|
|
{
|
|
current_time = 0;
|
|
}
|
|
percent_flash = current_time/fast_flash_time;
|
|
//self.armorhudelem.color = ( (1-percent_flash)*(1,1,1) ) + (percent_flash*fast_flash_color);
|
|
current_time += 0.05;
|
|
}
|
|
else
|
|
{
|
|
//self.armorhudelem.color = (1, 1, 1);
|
|
}
|
|
|
|
self setHealthPercent( percent );
|
|
/*
|
|
self.frontarmorhudelem setText( "front armor: "+self.frontarmor );
|
|
self.backarmorhudelem setText( "back armor: "+self.backarmor );
|
|
self.rightarmorhudelem setText( "right armor: "+self.rightarmor );
|
|
self.leftarmorhudelem setText( "left armor: "+self.leftarmor );
|
|
*/
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: regen_armor
|
|
// CALLED ON: player vehicle
|
|
// PURPOSE: This waits until the player has not been damaged for a sufficiently long time (set
|
|
// in see2::difficulty_scale ) and then begins armor regeneration.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
regen_armor()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self.time_since_last_damage = 0;
|
|
|
|
self thread update_damage_timer();
|
|
self thread wait_for_damage_events();
|
|
|
|
|
|
/*
|
|
timer_widget = undefined;
|
|
owner = self getVehicleOwner();
|
|
if( isDefined( owner ) && isPlayer( owner ) )
|
|
{
|
|
timer_widget = newclienthudelem( owner );
|
|
timer_widget.x = 20;
|
|
timer_widget.y = 20;
|
|
timer_widget settext( "Time before regen: 0" );
|
|
timer_widget.alpha = 0;
|
|
}
|
|
*/
|
|
|
|
while( 1 )
|
|
{
|
|
if( self.time_since_last_damage > level.time_before_regen )
|
|
{
|
|
/*
|
|
if( isDefined( timer_widget ) )
|
|
{
|
|
timer_widget.alpha = 0;
|
|
}
|
|
*/
|
|
self thread begin_armor_regen();
|
|
self waittill_either( "damage", "armor_full" );
|
|
}
|
|
/*
|
|
else if( isDefined( timer_widget ) )
|
|
{
|
|
timer_widget.alpha = 1;
|
|
timer_widget settext( "Time before regen: "+(level.time_before_regen - self.time_since_last_damage) );
|
|
}
|
|
*/
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: update_damage_timer
|
|
// CALLED ON: player vehicle
|
|
// PURPOSE: Increments the time since the player tank has last been damaged
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
update_damage_timer()
|
|
{
|
|
self endon( "death" );
|
|
while( 1 )
|
|
{
|
|
self.time_since_last_damage += 0.05;
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_damage_events
|
|
// CALLED ON: player vehicle
|
|
// PURPOSE: resets the damage timer to zero whenever the player tank is damaged
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_damage_events()
|
|
{
|
|
self endon( "death" );
|
|
while( 1 )
|
|
{
|
|
self waittill( "damage" );
|
|
self.time_since_last_damage = 0;
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////
|
|
// DIALOG SUPPORT
|
|
/////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: update_dialog_timer
|
|
// CALLED ON: level
|
|
// PURPOSE: Keeps track of the time since a dialog line has last been delivered
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
update_dialogue_timer()
|
|
{
|
|
while( 1 )
|
|
{
|
|
level.dialogue_timer += 0.05;
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_dialog_triggers
|
|
// CALLED ON: level
|
|
// PURPOSE: Creates a thread on all level dialog triggers waiting for them to trigger their
|
|
// dialog event
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_dialog_triggers()
|
|
{
|
|
wait_for_first_player();
|
|
|
|
dialog_triggers = GetEntArray( "dialog trigger", "script_noteworthy" );
|
|
for( i = 0; i < dialog_triggers.size; i++ )
|
|
{
|
|
dialog_triggers[i] thread wait_for_dialog_event();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_dialog_event
|
|
// CALLED ON: dialog trigger
|
|
// PURPOSE: Waits until the dialog trigger is triggered, then plays the sound for the event
|
|
// corresponding to its script_string (see ::init_level_flags below for examples of
|
|
// setting up events, their endon signals, complete signals and pertinent flags.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_dialog_event()
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", guy );
|
|
if( isPlayer( guy ) )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
do_sound_for_event( self.script_string );
|
|
if( isDefined( level.kill_events[ self.script_string ] ) )
|
|
{
|
|
for( i = 0; i < level.kill_events[ self.script_string ].size; i++ )
|
|
{
|
|
level notify( level.endon_signals[ level.kill_events[self.script_string][i] ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: init_level_flags
|
|
// CALLED ON: level
|
|
// PURPOSE: This initializes all level flags, including the ones used by the dialog system. The
|
|
// dialog system does this by adding entries indexed by event name into five arrays.
|
|
// The arrays and their purposes are:
|
|
// 1. event_flags = this contains an array of all the flags that are used for this event.
|
|
// These flags will be run through ::update_target_flag below whenever
|
|
// this event is activated through ::do_sound_for_event. They will also
|
|
// be passed into the function in the event_functions array. Maximum
|
|
// number of flags for an event is currently 6, this can be increased by
|
|
// creating more arguments in the function calls contained in see2_sound.gsc
|
|
// 2. endon_signals = This is the signal which, if the level is notified with, will end
|
|
// this dialogue function, stopping the playback of any future lines, but
|
|
// not any queued lines from the event.
|
|
// 3. complete_signals = This is the signal which, if the event completes successfully,
|
|
// will be sent to the level as a notify.
|
|
// 4. event_functions = This is the event function that will be called with all elements
|
|
// of the event_flags array as arguments when the sound event is kicked
|
|
// off.
|
|
// 5. kill_events = This is the events that will have their kill signals sent automatically
|
|
// when this event begins.
|
|
// ADDITIONS NEEDED: Add any new flags or events requested
|
|
/////////////////////////////////
|
|
init_level_flags()
|
|
{
|
|
//Skipto flags
|
|
level.skipto_flag_names = [];
|
|
flag_init( "flak 88s destroyed" );
|
|
level.skipto_flag_names = array_add( level.skipto_flag_names, "flak 88s destroyed" );
|
|
flag_init( "radio tower destroyed" );
|
|
level.skipto_flag_names = array_add( level.skipto_flag_names, "radio tower destroyed" );
|
|
flag_init( "fuel depot cleared" );
|
|
level.skipto_flag_names = array_add( level.skipto_flag_names, "fuel depot cleared" );
|
|
flag_init( "final line breached" );
|
|
level.skipto_flag_names = array_add( level.skipto_flag_names, "final line breached" );
|
|
|
|
//General dialog flags
|
|
flag_init( "playback happening" ); // used for queuing and resolving story events;
|
|
flag_init( "battlechatter allowed" ); // allows preemption of battlechatter when appropriate during
|
|
// story events
|
|
|
|
// Misc Flags
|
|
flag_init( "coop" );
|
|
flag_init( "left_path" );
|
|
|
|
//EVENT FLAGS
|
|
level.event_flags = [];
|
|
level.endon_signals = [];
|
|
level.complete_signals = [];
|
|
level.event_functions = [];
|
|
level.kill_events = [];
|
|
|
|
//Battlechatter
|
|
level.event_flags["battlechatter"] = [];
|
|
flag_init( "do_firing" );
|
|
level.event_flags["battlechatter"] = array_add( level.event_flags["battlechatter"], "do_firing" );
|
|
flag_init( "heavy_damage" );
|
|
level.event_flags["battlechatter"] = array_add( level.event_flags["battlechatter"], "heavy_damage" );
|
|
flag_init( "damaged" );
|
|
level.event_flags["battlechatter"] = array_add( level.event_flags["battlechatter"], "damaged" );
|
|
flag_init( "infantry_close" );
|
|
level.event_flags["battlechatter"] = array_add( level.event_flags["battlechatter"], "infantry_close" );
|
|
flag_init( "retreaters" );
|
|
level.event_flags["battlechatter"] = array_add( level.event_flags["battlechatter"], "retreaters" );
|
|
flag_init( "destruction" );
|
|
level.event_flags["battlechatter"] = array_add( level.event_flags["battlechatter"], "destruction" );
|
|
flag_init( "idle" );
|
|
level.event_flags["battlechatter"] = array_add( level.event_flags["battlechatter"], "idle" );
|
|
level.endon_signals["battlechatter"] = "end battlechatter";
|
|
level.complete_signals["battlechatter"] = "DNE";
|
|
level.event_functions["battlechatter"] = ::do_battlechatter;
|
|
|
|
|
|
//Destroy Artillery
|
|
level.event_flags["objective intro"] = [];
|
|
level.endon_signals["objective intro"] = "intro interrupt";
|
|
level.complete_signals["objective intro"] = "commissar done";
|
|
level.event_functions["objective intro"] = ::level_intro_announcements;
|
|
|
|
level.event_flags["flamethrower_tutorial"] = [];
|
|
flag_init( "flamethrower_fired_once" );
|
|
level.event_flags["flamethrower_tutorial"] = array_add( level.event_flags["flamethrower_tutorial"], "flamethrower_fired_once" );
|
|
flag_init( "ads_once" );
|
|
level.event_flags["flamethrower_tutorial"] = array_add( level.event_flags["flamethrower_tutorial"], "ads_once" );
|
|
flag_init( "flamethrower_close_to_inf" );
|
|
level.event_flags["flamethrower_tutorial"] = array_add( level.event_flags["flamethrower_tutorial"], "flamethrower_close_to_inf" );
|
|
flag_init( "killed_with_flamethrower" );
|
|
level.event_flags["flamethrower_tutorial"] = array_add( level.event_flags["flamethrower_tutorial"], "killed_with_flamethrower" );
|
|
level.endon_signals["flamethrower_tutorial"] = "engaged second 88";
|
|
level.complete_signals["flamethrower_tutorial"] = "tank weapon tutorial complete";
|
|
level.event_functions["flamethrower_tutorial"] = ::flamethrower_tutorial;
|
|
|
|
level.event_flags["first_88_obj"] = [];
|
|
level.event_flags["first_88_obj"] = array_add( level.event_flags["first_88_obj"], "destroyed first 88" );
|
|
level.endon_signals["first_88_obj"] = "moved into field";
|
|
level.complete_signals["first_88_obj"] = "first 88 destroyed";
|
|
level.event_functions["first_88_obj"] = ::first_88_obj;
|
|
|
|
level.event_flags["second_88_obj"] = [];
|
|
level.event_flags["second_88_obj"] = array_add( level.event_flags["second_88_obj"], "destroyed second 88" );
|
|
flag_init( "second 88 in sights" );
|
|
level.event_flags["second_88_obj"] = array_add( level.event_flags["second_88_obj"], "second 88 in sights" );
|
|
level.endon_signals["second_88_obj"] = "moved past second position";
|
|
level.complete_signals["second_88_obj"] = "second 88 destroyed";
|
|
level.event_functions["second_88_obj"] = ::second_88_obj;
|
|
|
|
level.event_flags["dead_88"] = [];
|
|
flag_init( "destroyed first 88" );
|
|
level.event_flags["dead_88"] = array_add( level.event_flags["dead_88"], "destroyed first 88" );
|
|
flag_init( "destroyed second 88" );
|
|
level.event_flags["dead_88"] = array_add( level.event_flags["dead_88"], "destroyed second 88" );
|
|
flag_init( "destroyed third and fourth 88" );
|
|
level.event_flags["dead_88"] = array_add( level.event_flags["dead_88"], "destroyed third and fourth 88" );
|
|
flag_init( "destroyed second last 88" );
|
|
level.event_flags["dead_88"] = array_add( level.event_flags["dead_88"], "destroyed second last 88" );
|
|
flag_init( "destroyed last 88" );
|
|
level.event_flags["dead_88"] = array_add( level.event_flags["dead_88"], "destroyed last 88" );
|
|
level.endon_signals["dead_88"] = "stop track 88";
|
|
level.complete_signals["dead_88"] = "all 88s dead";
|
|
level.event_functions["dead_88"] = ::dead_88;
|
|
|
|
level.event_flags["tank_move_fire_tutorial"] = [];
|
|
flag_init( "first_fired_on_event" );
|
|
level.event_flags["tank_move_fire_tutorial"] = array_add( level.event_flags["tank_move_fire_tutorial"], "first_fired_on_event" );
|
|
flag_init( "first_shot" );
|
|
level.event_flags["tank_move_fire_tutorial"] = array_add( level.event_flags["tank_move_fire_tutorial"], "first_shot" );
|
|
level.endon_signals["tank_move_fire_tutorial"] = "skipped mf tutorial";
|
|
level.complete_signals["tank_move_fire_tutorial"] = "tank move tutorial complete";
|
|
level.event_functions["tank_move_fire_tutorial"] = ::tank_reload_movement_tutorial;
|
|
|
|
level.event_flags["first_panther"] = [];
|
|
flag_init( "panther_activated" );
|
|
level.event_flags["first_panther"] = array_add( level.event_flags["first_panther"], "panther_activated" );
|
|
flag_init( "panther_in_sights" );
|
|
level.event_flags["first_panther"] = array_add( level.event_flags["first_panther"], "panther_in_sights" );
|
|
flag_init( "panther_first_shot" );
|
|
level.event_flags["first_panther"] = array_add( level.event_flags["first_panther"], "panther_first_shot" );
|
|
flag_init( "panther_second_shot" );
|
|
level.event_flags["first_panther"] = array_add( level.event_flags["first_panther"], "panther_second_shot" );
|
|
flag_init( "panther_third_shot" );
|
|
level.event_flags["first_panther"] = array_add( level.event_flags["first_panther"], "panther_third_shot" );
|
|
flag_init( "panther_dead" );
|
|
level.event_flags["first_panther"] = array_add( level.event_flags["first_panther"], "panther_dead" );
|
|
level.endon_signals["first_panther"] = "engage last 88s";
|
|
level.complete_signals["first_panther"] = "first panther dead";
|
|
level.event_functions["first_panther"] = ::first_panther_prompt;
|
|
|
|
level.event_flags["choose_path"] = [];
|
|
level.endon_signals["choose_path"] = "path chosen";
|
|
level.complete_signals["choose_path"] = "player chose... wisely";
|
|
level.event_functions["choose_path"] = ::choose_path;
|
|
|
|
level.event_flags["choose_right_path"] = [];
|
|
level.event_flags["choose_right_path"] = array_add( level.event_flags["choose_right_path"], "destroyed third and fourth 88" );
|
|
level.event_flags["choose_right_path"] = array_add( level.event_flags["choose_right_path"], "destroyed second last 88" );
|
|
level.event_flags["choose_right_path"] = array_add( level.event_flags["choose_right_path"], "destroyed last 88" );
|
|
level.endon_signals["choose_right_path"] = "stop choose right";
|
|
level.complete_signals["choose_right_path"] = "all artillery destroyed";
|
|
level.event_functions["choose_right_path"] = ::choose_right_path;
|
|
|
|
level.event_flags["choose_left_path"] = [];
|
|
level.event_flags["choose_left_path"] = array_add( level.event_flags["choose_left_path"], "destroyed third and fourth 88" );
|
|
level.event_flags["choose_left_path"] = array_add( level.event_flags["choose_left_path"], "destroyed second last 88" );
|
|
level.event_flags["choose_left_path"] = array_add( level.event_flags["choose_left_path"], "destroyed last 88" );
|
|
level.endon_signals["choose_left_path"] = "stop choose left";
|
|
level.complete_signals["choose_left_path"] = "all artillery destroyed";
|
|
level.event_functions["choose_left_path"] = ::choose_left_path;
|
|
|
|
level.event_flags["player_exposed"] = [];
|
|
flag_init( "internal_first_warning_given" );
|
|
level.event_flags["player_exposed"] = array_add( level.event_flags["player_exposed"], "internal_first_warning_given" );
|
|
flag_init( "internal_second_warning_given" );
|
|
level.event_flags["player_exposed"] = array_add( level.event_flags["player_exposed"], "internal_second_warning_given" );
|
|
level.endon_signals["player_exposed"] = "player not exposed";
|
|
level.complete_signals["player_exposed"] = "all warnings given";
|
|
level.event_functions["player_exposed"] = ::player_exposed;
|
|
|
|
// Radio tower
|
|
level.event_flags["radio_tower_dialog"] = [];
|
|
flag_init( "radio_tower_visible" );
|
|
level.event_flags["radio_tower_dialog"] = array_add( level.event_flags["radio_tower_dialog"], "radio_tower_visible" );
|
|
flag_init( "radio_tower_close" );
|
|
level.event_flags["radio_tower_dialog"] = array_add( level.event_flags["radio_tower_dialog"], "radio_tower_close" );
|
|
flag_init( "radio_tower_destroyed" );
|
|
level.event_flags["radio_tower_dialog"] = array_add( level.event_flags["radio_tower_dialog"], "radio_tower_destroyed" );
|
|
level.kill_events["radio_tower_dialog"] = [];
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "first_88_obj" );
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "second_88_obj" );
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "dead_88" );
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "first_panther" );
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "choose_path" );
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "choose_left_path" );
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "choose_right_path" );
|
|
level.kill_events["radio_tower_dialog"] = array_add( level.kill_events["radio_tower_dialog"], "player_exposed" );
|
|
level.endon_signals["radio_tower_dialog"] = "do not interrupt";
|
|
level.complete_signals["radio_tower_dialog"] = "radio tower destroyed";
|
|
level.event_functions["radio_tower_dialog"] = ::radio_tower_dialog;
|
|
|
|
// Fuel Depot
|
|
level.event_flags["fuel_depot_dialog"] = [];
|
|
level.endon_signals["fuel_depot_dialog"] = "do not interrupt";
|
|
level.complete_signals["fuel_depot_dialog"] = "fuel depot done";
|
|
level.event_functions["fuel_depot_dialog"] = ::fuel_depot_dialog;
|
|
|
|
// Final Battle
|
|
level.event_flags["final_battle_dialog"] = [];
|
|
level.endon_signals["final_battle_dialog"] = "do not interrupt";
|
|
level.complete_signals["final_battle_dialog"] = "final done";
|
|
level.event_functions["final_battle_dialog"] = ::final_battle_dialog;
|
|
|
|
// Victory
|
|
//level.event_flags["victory_dialog"] = [];
|
|
//level.endon_signals["victory_dialog"] = "do not interrupt";
|
|
//level.complete_signals["victory_dialog"] = "victory done";
|
|
//level.event_functions["victory_dialog"] = ::victory_dialog;
|
|
|
|
// GLocke: objective flags to check if the 2 optional objectives had been completed at the end of the map.
|
|
flag_init( "flak objective completed" );
|
|
flag_init( "radio tower objective completed" );
|
|
flag_init( "final battle already started" );
|
|
|
|
flag_init( "player_ready_for_outro" );
|
|
flag_init( "outro_group_1_ready" );
|
|
flag_init( "outro_group_2_ready" );
|
|
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_sound_for_event
|
|
// CALLED ON: level
|
|
// PURPOSE: This is the function called through script or through the dialog_trigger setup that
|
|
// will playback the sound for an event using the function setup specified in
|
|
// ::init_level_flags. It also does some very basic error checking to keep it from
|
|
// double threading events and running improperly setup events
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_sound_for_event( event )
|
|
{
|
|
if( !isDefined( event ) || !isDefined( level.event_flags[event] ) || !isDefined( level.endon_signals[event] ) || !isDefined( level.complete_signals[event] ) || !isDefined( level.event_functions[event] ) )
|
|
{
|
|
error( "Requested sound event '"+event+"' is not setup properly" );
|
|
}
|
|
|
|
if( !array_check_for_dupes( level.finished_events, event ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
level.finished_events = array_add( level.finished_events, event );
|
|
|
|
for( i = 0; i < level.event_flags[event].size; i++ )
|
|
{
|
|
if( !isSubStr( "internal_", level.event_flags[event][i] ) )
|
|
{
|
|
level thread update_target_flag( level.event_flags[event][i] );
|
|
}
|
|
}
|
|
level thread [[ level.event_functions[event] ]]( level.endon_signals[event], level.complete_signals[event], level.event_flags[event][0], level.event_flags[event][1], level.event_flags[event][2], level.event_flags[event][3], level.event_flags[event][4], level.event_flags[event][5], level.event_flags[event][6] );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: update_target_flag
|
|
// CALLED ON: level
|
|
// PURPOSE: This is threaded for each flag in a sound event. It is a giant switch statement that
|
|
// compartmentalizes all the logic for each individual flag in a case specified by the
|
|
// flag name
|
|
// ADDITIONS NEEDED: Add any needed flags for new events
|
|
/////////////////////////////////
|
|
update_target_flag( flag )
|
|
{
|
|
switch( flag )
|
|
{
|
|
//"heavy_damage"
|
|
case "heavy_damage":
|
|
player = get_players()[0];
|
|
while( 1 )
|
|
{
|
|
if( !isDefined( player.myTank ) )
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
player.myTank waittill( "damage", amount );
|
|
old_percent = player.myTank.armor/player.myTank.maxarmor;
|
|
new_percent = (player.myTank.armor-amount)/player.myTank.maxarmor;
|
|
if( (old_percent > 0.2 && new_percent <= 0.2) || ( old_percent > 0.5 && new_percent <= 0.5 ) )
|
|
{
|
|
flag_set( "heavy_damage" );
|
|
wait( 3 );
|
|
flag_clear( "heavy_damage" );
|
|
wait( 8 );
|
|
}
|
|
}
|
|
|
|
//"do_firing"
|
|
case "do_firing":
|
|
player = get_players()[0];
|
|
while( 1 )
|
|
{
|
|
if( !isDefined( player.myTank ) )
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
angles = player.myTank getTagAngles( "tag_flash" );
|
|
origin = player.myTank getTagOrigin( "tag_flash" );
|
|
vec = anglestoforward( angles );
|
|
|
|
trace = bullettrace( origin, origin + vec*5000, false, player.myTank );
|
|
if( trace["fraction"] < 0.95 )
|
|
{
|
|
ent = trace["entity"];
|
|
if( isDefined( ent ) )
|
|
{
|
|
if( !array_check_for_dupes( level.enemy_armor, ent ) && ent.classname != "script_vehicle_corpse" )
|
|
{
|
|
flag_set( "do_firing" );
|
|
}
|
|
}
|
|
}
|
|
|
|
wait( 4 );
|
|
}
|
|
|
|
//"damaged"
|
|
case "damaged":
|
|
player = get_players()[0];
|
|
while( 1 )
|
|
{
|
|
if( !isDefined( player.myTank ) )
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
player.myTank waittill( "damage", amount );
|
|
flag_set( "damaged" );
|
|
wait( 0.5 );
|
|
flag_clear( "damaged" );
|
|
wait( 8 );
|
|
}
|
|
|
|
//"infantry_close"
|
|
case "infantry_close":
|
|
player = get_players()[0];
|
|
while( 1 )
|
|
{
|
|
close_count = 0;
|
|
for( i = 0; i < level.enemy_infantry.size; i++ )
|
|
{
|
|
if( isDefined( level.enemy_infantry[i] ) )
|
|
{
|
|
if( distanceSquared( player.myTank.origin, level.enemy_infantry[i].origin ) < level.min_distance_for_close_infantry*level.min_distance_for_close_infantry )
|
|
{
|
|
close_count++;
|
|
}
|
|
}
|
|
}
|
|
if( close_count > level.min_close_infantry_for_warning )
|
|
{
|
|
flag_set( "infantry_close" );
|
|
wait( 3 );
|
|
flag_clear( "infantry_close" );
|
|
}
|
|
wait( 3 );
|
|
}
|
|
|
|
//"retreaters"
|
|
case "retreaters":
|
|
player = get_players()[0];
|
|
while( 1 )
|
|
{
|
|
level waittill( "retreaters", ent );
|
|
|
|
if( check_for_visible( player, ent, 3000 ) )
|
|
{
|
|
flag_set( "retreaters" );
|
|
wait( 4 );
|
|
flag_clear( "retreaters" );
|
|
}
|
|
}
|
|
|
|
//"destruction"
|
|
case "destruction":
|
|
player = get_players()[0];
|
|
while( 1 )
|
|
{
|
|
level waittill( "destruction" );
|
|
flag_set( "destruction" );
|
|
wait( 4 );
|
|
flag_clear("destruction" );
|
|
}
|
|
|
|
//"idle"
|
|
case "idle":
|
|
player = get_players()[0];
|
|
prev_position = player.origin;
|
|
|
|
while( 1 )
|
|
{
|
|
if( distanceSquared( prev_position, player.origin ) < level.min_idle_dist_sq )
|
|
{
|
|
level.current_idle_time += 0.05;
|
|
if( level.current_idle_time >= level.idle_warn_time )
|
|
{
|
|
level.current_idle_time = 0;
|
|
flag_set( "idle" );
|
|
wait( 4 );
|
|
flag_clear( "idle" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
level.current_idle_time = 0;
|
|
}
|
|
prev_position = player.origin;
|
|
}
|
|
|
|
//"flamethrower_fired_once"
|
|
case "flamethrower_fired_once":
|
|
players = get_players();
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
players[0] thread inform_on_ft_button( "ft_pressed" );
|
|
}
|
|
level waittill_notify_or_timeout( "ft_pressed", 6 );
|
|
players[0] notify( "go_past_ft_tut" );
|
|
flag_set( "flamethrower_fired_once" );
|
|
break;
|
|
|
|
//"ads_once"
|
|
case "ads_once":
|
|
players = get_players();
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
players[0] thread inform_on_ads_button( "ads_pressed" );
|
|
}
|
|
level waittill_notify_or_timeout( "ads_pressed", 14 ); //-- needs the extra time to wait out the ft tutorial
|
|
players[0] notify( "go_past_ads_tut" );
|
|
flag_set( "ads_once" );
|
|
break;
|
|
|
|
//"flamethrower_close_to_inf"
|
|
case "flamethrower_close_to_inf":
|
|
players = get_players();
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
players[i] thread check_close_to_stuck_inf( "player_close", 1200 );
|
|
}
|
|
level waittill( "player_close" );
|
|
flag_set( "flamethrower_close_to_inf" );
|
|
wait( 1 );
|
|
flag_set( "battlechatter allowed" );
|
|
break;
|
|
|
|
//"destroyed first 88"
|
|
case "destroyed first 88":
|
|
while( 1 )
|
|
{
|
|
if( (level.max_active_arty - level.active_arty) > 0 )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "destroyed first 88" );
|
|
break;
|
|
|
|
//"second 88 in sights"
|
|
case "second 88 in sights":
|
|
second_arty = getEnt( "arty 3", "targetname" );
|
|
in_player_sights = false;
|
|
while( in_player_sights == false )
|
|
{
|
|
if( !isDefined( second_arty ) )
|
|
{
|
|
break;
|
|
}
|
|
for( i = 0; i < get_players().size && !in_player_sights; i++ )
|
|
{
|
|
toVec = second_arty.origin - get_players()[i].origin;
|
|
forward = anglesToForward( get_players()[i].angles );
|
|
toVec = ( toVec[0], toVec[1], 0 );
|
|
forward = ( forward[0], forward[1], 0 );
|
|
diff = VectorDot( VectorNormalize( forward ), VectorNormalize( toVec ) );
|
|
if( acos( diff ) < 65 )
|
|
{
|
|
in_player_sights = true;
|
|
}
|
|
}
|
|
wait( 1 );
|
|
}
|
|
flag_set( "second 88 in sights" );
|
|
break;
|
|
|
|
//"destroyed second 88"
|
|
case "destroyed second 88":
|
|
while( 1 )
|
|
{
|
|
if( (level.max_active_arty - level.active_arty) > 1 )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "destroyed second 88" );
|
|
break;
|
|
|
|
//"destroyed third and fourth 88"
|
|
case "destroyed third and fourth 88":
|
|
while( 1 )
|
|
{
|
|
if( (level.max_active_arty - level.active_arty) > 3 )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "destroyed third and fourth 88" );
|
|
break;
|
|
|
|
//"destroyed second last 88"
|
|
case "destroyed second last 88":
|
|
while( 1 )
|
|
{
|
|
if( level.active_arty < 2 )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "destroyed second last 88" );
|
|
break;
|
|
|
|
//"destroyed last 88"
|
|
case "destroyed last 88":
|
|
while( 1 )
|
|
{
|
|
if( level.active_arty == 0 )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "destroyed last 88" );
|
|
break;
|
|
|
|
//"first_fired_on_event"
|
|
case "first_fired_on_event":
|
|
flag_set( "first_fired_on_event" );
|
|
break;
|
|
|
|
//"first_shot"
|
|
case "first_shot":
|
|
players = get_players();
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
players[i] thread wait_for_player_shoot_event( "player shot" );
|
|
}
|
|
level waittill( "player shot" );
|
|
flag_set( "first_shot" );
|
|
break;
|
|
|
|
//"panther_activated"
|
|
case "panther_activated":
|
|
trigger = GetEnt( "panther activate trigger", "targetname" );
|
|
trigger waittill( "trigger" );
|
|
wait( 8 );
|
|
flag_set( "panther_activated" );
|
|
|
|
//"panther_in_sights"
|
|
case "panther_in_sights":
|
|
flag_wait( "panther_activated" );
|
|
panther = getEnt( "lineveh 4 group0", "script_noteworthy" );
|
|
in_player_sights = false;
|
|
while( in_player_sights == false )
|
|
{
|
|
if( !isDefined( panther ) )
|
|
{
|
|
break;
|
|
}
|
|
for( i = 0; i < get_players().size && !in_player_sights; i++ )
|
|
{
|
|
toVec = panther.origin - get_players()[i].origin;
|
|
forward = anglesToForward( get_players()[i].angles );
|
|
toVec = ( toVec[0], toVec[1], 0 );
|
|
forward = ( forward[0], forward[1], 0 );
|
|
diff = VectorDot( VectorNormalize( forward ), VectorNormalize( toVec ) );
|
|
if( acos( diff ) < 30 )
|
|
{
|
|
in_player_sights = true;
|
|
}
|
|
}
|
|
wait( 1 );
|
|
}
|
|
flag_set( "panther_in_sights" );
|
|
break;
|
|
|
|
//"panther_first_shot"
|
|
case "panther_first_shot":
|
|
panther = getEnt( "lineveh 4 group0", "script_noteworthy" );
|
|
damaged_by_player = false;
|
|
while( !damaged_by_player )
|
|
{
|
|
if( panther.classname == "script_vehicle_corpse" || panther.health < 1 )
|
|
{
|
|
break;
|
|
}
|
|
panther waittill( "damage", amt, guy );
|
|
damaged_by_player = check_damaged_by_player( guy );
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "panther_first_shot" );
|
|
break;
|
|
|
|
//"panther_second_shot"
|
|
case "panther_second_shot":
|
|
panther = getEnt( "lineveh 4 group0", "script_noteworthy" );
|
|
flag_wait( "panther_first_shot" );
|
|
damaged_by_player = false;
|
|
while( !damaged_by_player )
|
|
{
|
|
if( panther.classname == "script_vehicle_corpse" || panther.health < 1 )
|
|
{
|
|
break;
|
|
}
|
|
panther waittill( "damage", amt, guy );
|
|
damaged_by_player = check_damaged_by_player( guy );
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "panther_second_shot" );
|
|
break;
|
|
|
|
//"panther_third_shot"
|
|
case "panther_third_shot":
|
|
panther = getEnt( "lineveh 4 group0", "script_noteworthy" );
|
|
flag_wait( "panther_first_shot" );
|
|
flag_wait( "panther_second_shot" );
|
|
damaged_by_player = false;
|
|
while( !damaged_by_player )
|
|
{
|
|
if( panther.classname == "script_vehicle_corpse" || panther.health < 1 )
|
|
{
|
|
break;
|
|
}
|
|
panther waittill( "damage", amt, guy );
|
|
damaged_by_player = check_damaged_by_player( guy );
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "panther_third_shot" );
|
|
break;
|
|
|
|
//"panther_dead"
|
|
case "panther_dead":
|
|
panther = getEnt( "lineveh 4 group0", "script_noteworthy" );
|
|
panther waittill( "death" );
|
|
flag_set( "panther_dead" );
|
|
break;
|
|
|
|
//"radio_tower_visible"
|
|
case "radio_tower_visible":
|
|
radio_tower = GetEnt( "radio tower", "script_noteworthy" );
|
|
visible = false;
|
|
while( !visible )
|
|
{
|
|
for( i = 0; i < get_players().size && !visible; i++ )
|
|
{
|
|
if( check_for_visible( get_players()[i], radio_tower, 8500 ) )
|
|
{
|
|
visible = true;
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
flag_set( "radio_tower_visible" );
|
|
break;
|
|
|
|
//"radio_tower_close"
|
|
case "radio_tower_close":
|
|
radio_tower = GetEnt( "radio tower", "script_noteworthy" );
|
|
close = false;
|
|
while( !close )
|
|
{
|
|
for( i = 0; i < get_players().size && !close; i++ )
|
|
{
|
|
if( check_for_close( get_players()[i], radio_tower, 3500 ) )
|
|
{
|
|
close = true;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
flag_set( "radio_tower_close" );
|
|
break;
|
|
|
|
//"radio_tower_destroyed"
|
|
case "radio_tower_destroyed":
|
|
radio_tower = GetEnt( "radio tower", "script_noteworthy" );
|
|
while( 1 )
|
|
{
|
|
if( radio_tower.model == "anim_seelow_radiotower_d" )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
wait( 3 );
|
|
flag_set( "radio_tower_destroyed" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_close
|
|
// CALLED ON: level
|
|
// PURPOSE: Helper function - checks to see if a player is within a specific distance of an object
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_for_close( player, object, dist )
|
|
{
|
|
if( distanceSquared( player.origin, object.origin ) < dist*dist )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_arty_destroyed
|
|
// CALLED ON: level
|
|
// PURPOSE: Helper function - Waits until an artillery in the passed array is destroyed then
|
|
// returns.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_for_arty_destroyed( arty_array, inform )
|
|
{
|
|
dead_arty = false;
|
|
while( !dead_arty )
|
|
{
|
|
for( i = 0; i < arty_array.size; i++ )
|
|
{
|
|
if( check_for_arty_dead( arty_array[i] ) )
|
|
{
|
|
dead_arty = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_visible
|
|
// CALLED ON: level
|
|
// PURPOSE: Helper function - Checks to see if a player is within a certain number of units of an
|
|
// object and if it is in the player's view arc.
|
|
// ADDITIONS NEEDED: Possibly add a trace confirmation at some point
|
|
/////////////////////////////////
|
|
check_for_visible( player, object, dist )
|
|
{
|
|
toVec = object.origin - player.origin;
|
|
forward = anglesToForward( player.angles );
|
|
toVec = ( toVec[0], toVec[1], 0 );
|
|
forward = ( forward[0], forward[1], 0 );
|
|
diff = VectorDot( VectorNormalize( forward ), VectorNormalize( toVec ) );
|
|
if( acos( diff ) < 65 && distanceSquared( player.origin, object.origin ) < dist * dist )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_either_arty_destroyed
|
|
// CALLED ON: level
|
|
// PURPOSE: Checks for either artillery passed being dead then gives a notify once one dies.
|
|
// ADDITIONS NEEDED: Maybe deprecate since it is mostly a special case of
|
|
// ::check_for_arty_destroyed
|
|
/////////////////////////////////
|
|
check_for_either_arty_destroyed( arty1, arty2, inform )
|
|
{
|
|
level endon( inform );
|
|
while( 1 )
|
|
{
|
|
if( check_for_arty_dead( arty1 ) || check_for_arty_dead( arty2 ) )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
level notify( inform );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_both_arty_destroyed
|
|
// CALLED ON: level
|
|
// PURPOSE: Checks to see that both arty are destroyed then notifies the level when they are
|
|
// ADDITIONS NEEDED: Maybe deprecate since it is mostly a special case of
|
|
// ::check for arty destroyed.
|
|
/////////////////////////////////
|
|
check_for_both_arty_destroyed( arty1, arty2, inform )
|
|
{
|
|
level endon( inform );
|
|
while( 1 )
|
|
{
|
|
if( check_for_arty_dead( arty1 ) && check_for_arty_dead( arty2 ) )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
level notify( inform );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_arty_dead
|
|
// CALLED ON: level
|
|
// PURPOSE: Checks to see if an artillery has no crew or has been destroyed somehow
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_for_arty_dead( arty )
|
|
{
|
|
if( !isDefined( arty ) || arty.health < 1 || arty.classname == "script_vehicle_corpse" || arty.crewsize < 1 )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_damaged_by_player
|
|
// CALLED ON: level
|
|
// PURPOSE: Helper function - Checks to see if the attacker was a player or his tank since
|
|
// vehicle damage gets attributed weirdly.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_damaged_by_player( guy )
|
|
{
|
|
damaged_by_player = false;
|
|
for( i = 0; i < get_players().size; i++ )
|
|
{
|
|
if( guy == get_players()[i] || guy == get_players()[i].myTank )
|
|
{
|
|
damaged_by_player = true;
|
|
}
|
|
}
|
|
return damaged_by_player;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_player_shoot_event
|
|
// CALLED ON: level
|
|
// PURPOSE: Helper Function - Waits until one player has attacked then sends a level notify
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_player_shoot_event( inform )
|
|
{
|
|
level endon( inform );
|
|
while( 1 )
|
|
{
|
|
if( self AttackButtonPressed() )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
level notify( inform );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_player_damage_event
|
|
// CALLED ON: level
|
|
// PURPOSE: Helper Function - Waits until one player has been damaged then sends a level notify
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_player_damage_event( inform )
|
|
{
|
|
level endon( inform );
|
|
self waittill( "tank hit" );
|
|
level notify( inform );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_close_to_stuck_inf
|
|
// CALLED ON: level
|
|
// PURPOSE: Checks to see if the player is within a certain distance of infantry with their
|
|
// script_noteworthy set to stuck infantry
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_close_to_stuck_inf( inform, dist )
|
|
{
|
|
stuck_infantry = [];
|
|
while( stuck_infantry.size < 1 )
|
|
{
|
|
stuck_infantry = get_living_ai_array( "stuck infantry", "script_noteworthy" );
|
|
wait( 0.05 );
|
|
}
|
|
|
|
close_enough = false;
|
|
while( !close_enough )
|
|
{
|
|
for( i = 0; i < stuck_infantry.size && !close_enough; i++ )
|
|
{
|
|
if( isDefined( stuck_infantry[i] ) )
|
|
{
|
|
if( distanceSquared( self.origin, stuck_infantry[i].origin ) < dist*dist)
|
|
{
|
|
close_enough = true;
|
|
}
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
level notify( inform );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: inform_on_ft_button
|
|
// CALLED ON: player
|
|
// PURPOSE: sends a level notify when the player has pressed their +frag button
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
inform_on_ft_button( inform )
|
|
{
|
|
while( 1 )
|
|
{
|
|
if( self FragButtonPressed() )
|
|
{
|
|
level notify( inform );
|
|
return;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: inform_on_ads_button
|
|
// CALLED ON: player
|
|
// PURPOSE: sends a level notify when the player has pressed their +speed_throw button
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
inform_on_ads_button( inform )
|
|
{
|
|
while( 1 )
|
|
{
|
|
if( self AdsButtonPressed() )
|
|
{
|
|
level notify( inform );
|
|
return;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
//////////////////////////
|
|
// INFANTRY
|
|
//////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: spawn_guys
|
|
// CALLED ON: anything
|
|
// PURPOSE: spawns infantry from an array of spawners, using network gating
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
spawn_guys( spawners, target_name, ok_to_spawn )
|
|
{
|
|
guys = [];
|
|
|
|
for( i = 0; i < spawners.size; i++ )
|
|
{
|
|
if(NumRemoteClients() == 0) //-- If there are no remote clients, then do not use OkToSpawn
|
|
{
|
|
guy = spawn_guy( spawners[i], target_name, false );
|
|
}
|
|
else
|
|
{
|
|
guy = spawn_guy( spawners[i], target_name, ok_to_spawn );
|
|
}
|
|
if( IsDefined( guy ) )
|
|
{
|
|
guys[guys.size] = guy;
|
|
}
|
|
}
|
|
|
|
// We do not want to return the guys if ok_to_spawn is used. Since a guy in the array may be dead.
|
|
// So, only return the guys array if we do not want to ok_to_spawn.
|
|
if( !IsDefined( ok_to_spawn ) || !ok_to_spawn )
|
|
{
|
|
return guys;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: spawn_guy
|
|
// CALLED ON: anything
|
|
// PURPOSE: spawns a guy, potentially observing networking
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
spawn_guy( spawner, target_name, ok_to_spawn )
|
|
{
|
|
if( IsDefined( ok_to_spawn ) && ok_to_spawn )
|
|
{
|
|
while( !OkToSpawn() )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
}
|
|
|
|
if( IsDefined( spawner.script_forcespawn ) && spawner.script_forcespawn )
|
|
{
|
|
guy = spawner StalingradSpawn();
|
|
}
|
|
else
|
|
{
|
|
guy = spawner DoSpawn();
|
|
}
|
|
|
|
if( !spawn_failed( guy ) )
|
|
{
|
|
if( IsDefined( target_name ) )
|
|
{
|
|
guy.targetname = target_name;
|
|
}
|
|
|
|
return guy;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_floodspawners
|
|
// CALLED ON: level
|
|
// PURPOSE: Does network gated floodspawning for the radio tower area
|
|
// ADDITIONS NEEDED: Generalize so it can be used at the train station as well
|
|
/////////////////////////////////
|
|
do_floodspawners( areaname, time )
|
|
{
|
|
trigger = getEnt( "radio tower back half", "script_noteworthy" );
|
|
|
|
level endon( trigger.script_noteworthy );
|
|
|
|
trigger thread inform_on_touch_trigger( trigger.script_noteworthy );
|
|
|
|
floodspawners = getEntArray( areaname+" floodspawner", "script_noteworthy" );
|
|
spawners = [];
|
|
|
|
|
|
//NETWORK: throttle for only 2+ player (changed to throttle all the time because of it was slowing down the game);
|
|
//flood_spawner_max_size = level.max_ai_spawn_current;
|
|
//if(get_players().size > 1)
|
|
//{
|
|
// flood_spawner_max_size = 24; //32 for single player
|
|
//}
|
|
|
|
x = 0; //-- we want to force the spawners the first time
|
|
y = 0;
|
|
|
|
while( 1 )
|
|
{
|
|
ai = getaiarray( "axis" );
|
|
|
|
if( ( ai.size < level.max_ai_spawn_current || x < 5 ) && y < 10)
|
|
{
|
|
spawners[0] = floodspawners[randomint(floodspawners.size)];
|
|
spawn_guys( spawners, undefined, true );
|
|
spawners[0].count++;
|
|
x++;
|
|
y++;
|
|
}
|
|
wait( time );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: fuel_depot_infantry_evasion
|
|
// CALLED ON: an infantryman
|
|
// PURPOSE: This causes the infantry in the fuel depot to choose a retreat position and take it
|
|
// when the player hits a trigger
|
|
// ADDITIONS NEEDED: Generalize this function and improve the name
|
|
/////////////////////////////////
|
|
fuel_depot_infantry_evasion()
|
|
{
|
|
self endon( "death" );
|
|
|
|
trigger = GetEnt( "fuel depot retreat trigger", "script_noteworthy" );
|
|
|
|
trigger waittill( "trigger" );
|
|
|
|
wait( randomfloatrange( 0.5, 3 ) );
|
|
|
|
retreat_node = level.fuel_depot_goal_nodes[ randomint( level.fuel_depot_goal_nodes.size ) ];
|
|
|
|
self setGoalNode( retreat_node );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: enforce_flame_deaths
|
|
// CALLED ON: an infantryman
|
|
// PURPOSE: Since the damageweapon for the player tank is misattributed, this tries to override
|
|
// it and force a flame death whenever the AI receives MOD_BURNED damage.
|
|
// ADDITIONS NEEDED: This currently doesn't work consistently, need to figure out how to prevent
|
|
// subsequent frame's damage from overriding the flame death anim
|
|
/////////////////////////////////
|
|
enforce_flame_deaths()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "damage", amount, other, direction_vec, point, type );
|
|
if( type == "MOD_BURNED" )
|
|
{
|
|
self.a.forceflamedeath = true;
|
|
self doDamage( self.health * 2, self.origin );
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: radio_tower_infantry_evasion
|
|
// CALLED ON: an infantryman
|
|
// PURPOSE: This causes an infantryman to shift between a variety of cover nodes and try to stay
|
|
// out of player view arcs.
|
|
// ADDITIONS NEEDED: This should be generalized and renamed for application elsewhere
|
|
/////////////////////////////////
|
|
radio_tower_infantry_evasion()
|
|
{
|
|
self endon( "death" );
|
|
|
|
players = get_players();
|
|
timer = 0;
|
|
min_time_before_shift = 6;
|
|
max_time_before_shift = 12;
|
|
|
|
while( 1 )
|
|
{
|
|
inArc = false;
|
|
for( i = 0; i < players.size && !inArc; i++ )
|
|
{
|
|
if( isDefined( players[i] ) )
|
|
{
|
|
inArc = self is_in_player_arc( players[i] );
|
|
}
|
|
}
|
|
if( timer > randomfloatrange( min_time_before_shift, max_time_before_shift ) || ( timer > min_time_before_shift && inArc ) )
|
|
{
|
|
//self setGoalNode( level.radio_tower_goal_nodes[ randomint( level.radio_tower_goal_nodes.size ) ] );
|
|
self notify("radio_set_new_goal_node");
|
|
self waittill( "goal" );
|
|
}
|
|
timer += 0.5;
|
|
wait( 0.5 );
|
|
}
|
|
}
|
|
|
|
|
|
radio_tower_infantry_grenades()
|
|
{
|
|
self endon("death");
|
|
|
|
while( 1 )
|
|
{
|
|
|
|
self waittill( "radio_set_new_goal_node" );
|
|
|
|
if( self.classname != "actor_axis_ger_ber_wehr_reg_kar98k" )
|
|
{
|
|
self setGoalNode( level.radio_tower_goal_nodes[ randomint( level.radio_tower_goal_nodes.size ) ] );
|
|
return;
|
|
}
|
|
|
|
random_grenade_chance = RandomIntRange(0, 100);
|
|
|
|
if( random_grenade_chance > 70 )
|
|
{
|
|
players = get_players();
|
|
|
|
my_target = players[0].myTank;
|
|
for( i=1; i < players.size; i++ )
|
|
{
|
|
if( distanceSquared(self.origin, my_target.origin) > distanceSquared(self.origin, players[i].myTank.origin) )
|
|
{
|
|
my_target = players[i].myTank;
|
|
}
|
|
}
|
|
|
|
self maps\_grenade_toss::force_grenade_toss( my_target.origin, undefined, 3, undefined, undefined );
|
|
}
|
|
|
|
self setGoalNode( level.radio_tower_goal_nodes[ randomint( level.radio_tower_goal_nodes.size ) ] );
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_antitank_AI
|
|
// CALLED ON: an infantryman
|
|
// PURPOSE: This causes panzershreck guys to manually target the tank rather than the player camera
|
|
// position.
|
|
// ADDITIONS NEEDED: This should be fleshed out into better behavior for small arms guys and should
|
|
// make panzershreck guys a little less deadly.
|
|
/////////////////////////////////
|
|
do_antitank_AI()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
best_target = self maps\_vehicle::get_nearest_target( level.player_tanks );
|
|
if( isDefined( best_target ) )
|
|
{
|
|
dist = distanceSquared( best_target.origin, self.origin );
|
|
if( dist < level.max_panzerschreck_eng_distsq && dist > level.min_panzerschreck_eng_distsq )
|
|
{
|
|
self setEntityTarget( best_target );
|
|
}
|
|
else
|
|
{
|
|
self clearEntityTarget();
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////
|
|
// MISS STRUCT TARGETING
|
|
//////////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: remove_old_targeters
|
|
// CALLED ON: an entity with miss structs
|
|
// PURPOSE: This removes entity A from the list of entities targeting the miss struct entity B
|
|
// allowing another entity to request entity B as a target
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
remove_old_targeters()
|
|
{
|
|
self endon( "death" );
|
|
while( 1 )
|
|
{
|
|
self waittill( "stopped targeting", ent );
|
|
for( i = 0; i < self.current_targeters.size; i++ )
|
|
{
|
|
if( self.current_targeters[i] == ent )
|
|
{
|
|
self.current_targeters = array_remove( self.current_targeters, ent );
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < self.queued_targeters.size; i++ )
|
|
{
|
|
if( self.queued_targeters[i] == ent )
|
|
{
|
|
self.queued_targeters = array_remove( self.queued_targeters, ent );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: update_current_targeters
|
|
// CALLED ON: an entity
|
|
// PURPOSE: This creates an array of x miss structs and gives the closest targeters a direct target
|
|
// of the miss_struct entity on a frame by frame basis, otherwise tells them to target
|
|
// the miss structs
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
update_current_targeters()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
self thread remove_old_targeters();
|
|
if( !isDefined( self.current_targeters ) )
|
|
{
|
|
self.current_targeters = [];
|
|
}
|
|
if( !isDefined( self.queued_targeters ) )
|
|
{
|
|
self.queued_targeters = [];
|
|
}
|
|
if( !isDefined( self.miss_structs ) )
|
|
{
|
|
self.miss_structs = [];
|
|
}
|
|
|
|
x_sign = 1;
|
|
y_sign = 1;
|
|
|
|
for( i = 0; i < level.number_miss_structs; i++ )
|
|
{
|
|
x = randomfloat( 0-level.max_miss_distance, level.max_miss_distance );
|
|
y = randomfloat( 0-level.max_miss_distance, level.max_miss_distance );
|
|
if( x < level.min_miss_distance && x > 0 )
|
|
{
|
|
x = level.min_miss_distance;
|
|
}
|
|
else if( x > 0-level.min_miss_distance )
|
|
{
|
|
x = 0-level.min_miss_distance;
|
|
}
|
|
|
|
if( y < level.min_miss_distance && x > 0 )
|
|
{
|
|
y = level.min_miss_distance;
|
|
}
|
|
else if( y > 0-level.min_miss_distance )
|
|
{
|
|
y = 0-level.min_miss_distance;
|
|
}
|
|
miss_struct = spawn( "script_origin", (0,0,0) );
|
|
miss_struct.origin = (self.origin[0]+x, self.origin[1]+y, self.origin[2]);
|
|
miss_struct.origin = groundpos( miss_struct.origin );
|
|
self.miss_structs = array_add( self.miss_structs, miss_struct );
|
|
miss_struct linkto( self );
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
best_dist = 10000000000;
|
|
best_index = -1;
|
|
for( i = 0; i < level.see2_max_targeters; i++ )
|
|
{
|
|
if( !isDefined( self.current_targeters[i] ) )
|
|
{
|
|
for( j = 0; j < self.queued_targeters.size; j++ )
|
|
{
|
|
if( isDefined( self.queued_targeters[j] ) )
|
|
{
|
|
dist = distanceSquared( self.origin, self.queued_targeters[i].origin );
|
|
if( dist < best_dist )
|
|
{
|
|
best_index = j;
|
|
best_dist = dist;
|
|
}
|
|
}
|
|
}
|
|
if( best_index > 0 )
|
|
{
|
|
self.queued_targeters[best_index] notify( "switch targets", self );
|
|
self.current_targeters[i] = self.queued_targeters[best_index];
|
|
self.queued_targeters = array_remove( self.queued_targeters, self.queued_targeters[best_index] );
|
|
}
|
|
}
|
|
}
|
|
wait( 0.5 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: request_target
|
|
// CALLED ON: an entity
|
|
// PURPOSE: This allows an entity to request the target of a miss struct entity and receive
|
|
// a miss struct if there are already too many entities targeting that entity.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
request_target( target )
|
|
{
|
|
if( !isDefined( target ) || !isDefined( target.miss_structs ) )
|
|
{
|
|
return target;
|
|
}
|
|
|
|
targeted = undefined;
|
|
for( i = 0; i < level.see2_max_targeters; i++ )
|
|
{
|
|
if( !isDefined( target.current_targeters[i] ) || target.current_targeters[i] == self )
|
|
{
|
|
target.current_targeters[i] = self;
|
|
targeted = target;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !isDefined( targeted ) )
|
|
{
|
|
rand = randomint( level.number_miss_structs );
|
|
targeted = target.miss_structs[rand];
|
|
if( array_check_for_dupes( target.queued_targeters, self ) )
|
|
{
|
|
target.queued_targeters = array_add( target.queued_targeters, self );
|
|
}
|
|
}
|
|
|
|
return targeted;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////
|
|
// EVENT 1
|
|
/////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: field_begin
|
|
// CALLED ON: level
|
|
// PURPOSE: This sets up all the variables for a start in area 1 where you destroy the artillery
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
field_begin()
|
|
{
|
|
thread setup_stuck_event();
|
|
|
|
thread setup_early_tanks();
|
|
|
|
wait_for_first_player();
|
|
wait( 4 );
|
|
|
|
|
|
players = get_players();
|
|
//4x4 Achievement script
|
|
for(i=0; i < players.size; i++)
|
|
{
|
|
players[i].squishcount = 0;
|
|
}
|
|
//-- Switch out the other players weapons
|
|
for(i = 1; i < players.size; i++)
|
|
{
|
|
players[i] TakeWeapon("m2_flamethrower");
|
|
players[i] SwitchToWeapon( "ppsh" );
|
|
}
|
|
|
|
setup_player_tanks();
|
|
|
|
thread keep_track_of_achievement();
|
|
|
|
thread do_sound_for_event( "battlechatter" );
|
|
|
|
do_area_spawn( 0 );
|
|
wait_network_frame();
|
|
do_arty_spawn( 0 );
|
|
|
|
arty_flaps = getEntArray( "arty tarp", "targetname" );
|
|
for( i = 0; i < arty_flaps.size; i++ )
|
|
{
|
|
arty_flaps[i] thread do_tarp_flap();
|
|
arty_flaps[i] thread cleanup_tarp();
|
|
}
|
|
|
|
level waittill( "controls_active" );
|
|
|
|
level clientNotify("aaa_begin");
|
|
level notify("aaa_begin");
|
|
level clientNotify("start_distance_planes_field_1");
|
|
level notify("start_distance_planes_field_1");
|
|
level thread field_begin_planes_end();
|
|
|
|
level thread arcademode_water_tower_setup();
|
|
|
|
|
|
arty_array = getEntArray( "group1 arty", "script_noteworthy" );
|
|
assert( arty_array.size > 1 );
|
|
objective_add(1, "current" );
|
|
|
|
for( i = 0; i < arty_array.size; i++ )
|
|
{
|
|
arty_array[i].health = 1;
|
|
arty_array[i] thread arty_behavior();
|
|
objective_additionalPosition( 1, i, arty_array[i].origin );
|
|
}
|
|
|
|
level.active_arty = arty_array.size;
|
|
level.max_active_arty = arty_array.size;
|
|
objective_string( 1, &"SEE2_DESTROY_ARTILLERY", level.active_arty );
|
|
|
|
third_tier = GetEntArray( "third tier", "targetname" );
|
|
third_tier = array_merge( third_tier, GetEntArray( "third tier two", "targetname" ) );
|
|
third_tier = array_merge( third_tier, GetEntArray( "third tier three", "targetname" ) );
|
|
|
|
for( i = 0; i < third_tier.size; i++ )
|
|
{
|
|
third_tier[i].script_forcespawn = 1;
|
|
}
|
|
|
|
for( i = 0; i < arty_array.size; i++ )
|
|
{
|
|
arty_array[i] thread wait_for_neutralize(i);
|
|
}
|
|
|
|
level thread wait_for_third_wave();
|
|
level thread wait_for_area_two_infantry();
|
|
level thread wait_for_area_three_infantry();
|
|
level thread wait_for_area_four_infantry();
|
|
level thread setup_airstrike_triggers();
|
|
|
|
//level thread move_radio_tower_blocker( false );
|
|
|
|
level thread do_retreat_trucks();
|
|
level thread do_stop_wait_triggers();
|
|
|
|
thread do_bunker_group( "first bunker", "fourth bunker", "first bunker taken" );
|
|
thread do_bunker_group( "second bunker", "fourth bunker", "second bunker taken", "second tier" );
|
|
thread do_bunker_group( "third bunker", "fifth bunker", "third bunker taken", "third tier" );
|
|
thread do_bunker_group( "fourth bunker", "third bunker", "fourth bunker taken", "second tier" );
|
|
thread do_bunker_group( "fifth bunker", "third bunker", "fifth bunker taken" );
|
|
|
|
thread wait_for_group_spawn( 1 );
|
|
thread wait_for_group_spawn( 2 );
|
|
thread wait_for_group_spawn( 3 );
|
|
thread keep_grenade_bags_from_spawning();
|
|
thread do_fake_schrecks();
|
|
|
|
//TUEY Set Music State to INTRO
|
|
setmusicstate("INTRO");
|
|
setbusstate("TANKS");
|
|
|
|
|
|
build_retreat_generics();
|
|
}
|
|
|
|
arcademode_water_tower_setup()
|
|
{
|
|
water_tower_trigs = [];
|
|
water_tower_trigs = GetEntArray("water_tower_trig", "targetname");
|
|
|
|
for(i = 0; i < water_tower_trigs.size; i++)
|
|
{
|
|
water_tower_trigs[i] thread arcademode_water_tower_give_points();
|
|
}
|
|
}
|
|
|
|
arcademode_water_tower_give_points()
|
|
{
|
|
self waittill( "trigger", ent );
|
|
|
|
if( IsPlayer( ent ) )
|
|
{
|
|
arcademode_assignpoints( "arcademode_score_banzai", ent );
|
|
}
|
|
}
|
|
|
|
move_radio_tower_blocker( proper_position )
|
|
{
|
|
radio_tower_blocker = GetEnt("radio_tower_blocker", "targetname");
|
|
if(!proper_position)
|
|
{
|
|
radio_tower_blocker.origin = radio_tower_blocker.origin - (0,0,5000);
|
|
}
|
|
else
|
|
{
|
|
radio_tower_blocker.origin = radio_tower_blocker.origin + (0,0,5000);
|
|
}
|
|
}
|
|
|
|
field_begin_planes_end()
|
|
{
|
|
trig = GetEnt("stop field planes", "targetname");
|
|
trig waittill("trigger");
|
|
|
|
level clientNotify("start_distance_planes_field_1_stop");
|
|
level notify("start_distance_planes_field_1_stop");
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_stop_wait_triggers
|
|
// CALLED ON: level
|
|
// PURPOSE: This sets up all the subsequent retreat triggers for the opel trucks in the level
|
|
// ADDITIONS NEEDED:
|
|
/////////////////////////////////
|
|
do_stop_wait_triggers()
|
|
{
|
|
triggers = getEntArray( "stop wait trigger", "targetname" );
|
|
for( i = 0; i < triggers.size; i++ )
|
|
{
|
|
triggers[i] thread wait_for_stop_wait();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_stop_wait
|
|
// CALLED ON: a retreater truck
|
|
// PURPOSE: This waits until a stop wait trigger is hit and the trucks continue their retreat.
|
|
// ADDITIONS NEEDED:
|
|
/////////////////////////////////
|
|
wait_for_stop_wait()
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", guy );
|
|
if( isPlayer( guy ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( array_check_for_dupes( level.invalid_retreat_points, self.script_noteworthy ) )
|
|
{
|
|
level.invalid_retreat_points = array_add( level.invalid_retreat_points, self.script_noteworthy );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_third_wave
|
|
// CALLED ON: level
|
|
// PURPOSE: This waits until the player has proceeded sufficiently then tries to spawn
|
|
// the third tier of bunker guards in the artillery area
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_third_wave()
|
|
{
|
|
spawners = getEntArray( "third tier", "targetname" );
|
|
|
|
level waittill_either( "second bunker taken", "fourth bunker taken" );
|
|
spawn_guys( spawners, undefined, true );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_neutralize
|
|
// CALLED ON: a flak 88
|
|
// PURPOSE: This waits until an artillery is destroyed or has had its crew killed. It then
|
|
// updates the "Destroy artillery" objective
|
|
// ADDITIONS NEEDED:
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//////////////////////////////////
|
|
wait_for_neutralize( objNum )
|
|
{
|
|
self waittill_either( "death", "crew dead" );
|
|
level.active_arty--;
|
|
if( level.active_arty == 0 )
|
|
{
|
|
Objective_AdditionalPosition( 1, objNum, ( 0, 0, 0 ) );
|
|
Objective_String_NoMessage( 1, &"SEE2_DESTROY_ARTILLERY", level.active_arty );
|
|
flag_set( "flak objective completed" );
|
|
Objective_State( 1, "done" );
|
|
autosave_by_name( "first area arty destroyed" );
|
|
level notify( "begin event 2" );
|
|
wait( 5 );
|
|
thread do_sound_for_event( "radio_tower_dialog" );
|
|
}
|
|
else
|
|
{
|
|
Objective_AdditionalPosition( 1, objNum, ( 0, 0, 0 ) );
|
|
Objective_String( 1, &"SEE2_DESTROY_ARTILLERY", level.active_arty );
|
|
if(level.active_arty == 2)
|
|
{
|
|
autosave_by_name( "first area arty destroyed"+ level.active_arty );
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_death_tank_sequence
|
|
// CALLED ON: the death tank from ::setup_stuck_event
|
|
// PURPOSE: This does the intial tank in front of the player rolling out and getting pwned by
|
|
// the panzershreck group near the first artillery
|
|
// ADDITIONS NEEDED: The panzershreck guys are currently starting to target the player instead
|
|
// of the death tank, this should be investigated and fixed.
|
|
/////////////////////////////////
|
|
do_death_tank_sequence( target_node, death_node )
|
|
{
|
|
self setWaitNode( target_node );
|
|
|
|
self waittill( "reached_wait_node" );
|
|
|
|
has_target = false;
|
|
|
|
stuck_infantry = [];
|
|
while( stuck_infantry.size < 1 )
|
|
{
|
|
stuck_infantry = get_living_ai_array( "stuck infantry", "script_noteworthy" );
|
|
wait( 0.05 );
|
|
}
|
|
|
|
for( i = 0; i < stuck_infantry.size; i++ )
|
|
{
|
|
if( isDefined( stuck_infantry[i] ) )
|
|
{
|
|
if( !has_target )
|
|
{
|
|
has_target = true;
|
|
self setTurretTargetEnt( stuck_infantry[i] );
|
|
}
|
|
stuck_infantry[i].ignoreall = false;
|
|
stuck_infantry[i] setEntityTarget( self );
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
|
|
self setWaitNode( death_node );
|
|
wait( 0.05 );
|
|
self waittill( "reached_wait_node" );
|
|
|
|
//self thread stop_keep_tank_alive();
|
|
|
|
self.health = 1;
|
|
|
|
self waittill( "death" );
|
|
flag_set( "death tank dead" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_stuck_event
|
|
// CALLED ON: level
|
|
// PURPOSE: This does all the setup for the do_death_tank_event event
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_stuck_event()
|
|
{
|
|
flag_init( "death tank dead" );
|
|
|
|
stuck_infantry = GetEntArray( "stuck infantry", "script_noteworthy" );
|
|
|
|
/*
|
|
for( i = 0; i < stuck_infantry.size; i++ )
|
|
{
|
|
stuck_infantry[i].ignoreall = true;
|
|
}
|
|
*/
|
|
|
|
level waittill( "controls_active" );
|
|
|
|
stuck_tanknode = GetEnt( "stuck tank node", "script_noteworthy" );
|
|
stuck_flee_nodes = GetNodeArray( "stuck flee node", "script_noteworthy" );
|
|
stuck_flee_panzer_nodes = GetNodeArray( "stuck panzer flee node", "script_noteworthy" );
|
|
stuck_trigger = GetEnt( "stuck trigger", "script_noteworthy" );
|
|
stuck_ammo_boxes = GetEntArray( "stuck ammo crate", "script_noteworthy" );
|
|
stuck_damage_trigger = GetEnt( "stuck damage trigger", "script_noteworthy" );
|
|
death_tank_move_trigger = getEnt( "death tank move_trigger", "script_noteworthy" );
|
|
death_tank_spawn_trigger = getEnt( "death tank spawn_trigger", "script_noteworthy" );
|
|
target_node = getVehicleNode( "start targeting", "script_noteworthy" );
|
|
death_node = getVehicleNode( "death node", "script_noteworthy" );
|
|
|
|
/*
|
|
for( i = 0; i < stuck_infantry.size; i++ )
|
|
{
|
|
stuck_infantry[i].goalradius = 32;
|
|
stuck_infantry[i] setGoalNode( GetNode( stuck_infantry[i].target, "targetname" ) );
|
|
stuck_infantry[i].ignoreall = true;
|
|
stuck_infantry[i] thread setup_bunker_infantry();
|
|
}
|
|
*/
|
|
|
|
array_thread( stuck_infantry, ::add_spawn_function, ::setup_stuck_infantry );
|
|
|
|
death_tank_spawn_trigger notify( "trigger" );
|
|
wait( 0.05 );
|
|
death_tank = getEnt( "death tank", "script_noteworthy" );
|
|
|
|
//death_tank thread keep_tank_alive();
|
|
|
|
death_tank_move_trigger notify( "trigger" );
|
|
|
|
death_tank thread do_death_tank_sequence( target_node, death_node );
|
|
|
|
stuck_trigger waittill( "trigger" );
|
|
|
|
while( !flag( "death tank dead" ) )
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
|
|
//do_sound_for_event( "tank_move_fire_tutorial" );
|
|
|
|
wait( 2 );
|
|
|
|
stuck_infantry = get_living_ai_array( "stuck infantry", "script_noteworthy" );
|
|
|
|
for( i = 0; i < stuck_infantry.size; i++ )
|
|
{
|
|
if( isDefined( stuck_infantry[i] ) )
|
|
{
|
|
stuck_infantry[i] ClearEntityTarget();
|
|
stuck_infantry[i] thread setup_bunker_infantry();
|
|
stuck_infantry[i].goalradius = 32;
|
|
if( stuck_infantry[i].classname == "actor_axis_ger_ber_wehr_reg_panzerschrek" )
|
|
{
|
|
stuck_infantry[i].maxsightdistsqrd = 3000*3000;
|
|
stuck_infantry[i] setGoalNode( stuck_flee_panzer_nodes[i] );
|
|
}
|
|
else
|
|
{
|
|
stuck_infantry[i] setGoalNode( stuck_flee_nodes[i] );
|
|
stuck_infantry[i].script_noteworthy = "first bunker guard";
|
|
}
|
|
wait( randomfloatrange( 0.25, 0.5 ) );
|
|
}
|
|
}
|
|
|
|
stuck_damage_trigger thread inform_on_damage_trigger( "blow up ammo boxes" );
|
|
|
|
level waittill( "blow up ammo boxes" );
|
|
|
|
for( i = 0; i < stuck_ammo_boxes.size; i++ )
|
|
{
|
|
playfx( level._effect["dummy_tank_explode"], stuck_ammo_boxes[i].origin );
|
|
radiusDamage( stuck_ammo_boxes[i].origin, 1000, 500, 500 );
|
|
stuck_ammo_boxes[i] delete();
|
|
wait( randomintrange( 1, 5 ) );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_stuck_infantry
|
|
// CALLED ON: a member of the death tank infantry
|
|
// PURPOSE: Sets up some basic variables for this entity
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_stuck_infantry()
|
|
{
|
|
self.goalradius = 32;
|
|
self.ignoreall = true;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_field_ambushes
|
|
// CALLED ON: level
|
|
// PURPOSE: sets up guys in fields with panzerschrecks
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_field_ambushes()
|
|
{
|
|
for( i = 1;; i++ )
|
|
{
|
|
trigger = GetEnt( "field "+i+" ambush trigger" );
|
|
if( isDefined( trigger ) )
|
|
{
|
|
level thread setup_single_ambush( i, trigger );
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_single_ambush
|
|
// CALLED ON: ambush trigger
|
|
// PURPOSE: sets up a single field ambush
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_single_ambush( field, trigger )
|
|
{
|
|
guys = GetEntArray( "field "+field+" ambush", "script_noteworthy" );
|
|
for( i = 0; i < guys.size; i++ )
|
|
{
|
|
guys.ignoreall = true;
|
|
guys.ignoreme = true;
|
|
guys allowedstances( "prone" );
|
|
guys.a.no_switch_weapon = true;
|
|
}
|
|
trigger inform_on_touch_trigger( trigger.script_noteworthy );
|
|
level waittill( trigger.script_noteworthy );
|
|
for( i = 0; i < guys.size; i++ )
|
|
{
|
|
guys.ignoreall = false;
|
|
guys.ignoreme = false;
|
|
guys allowedstances( "stand", "crouch", "prone" );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: find_farthest_retreat_point
|
|
// CALLED ON: Bunker guards whose positions have been overrun
|
|
// PURPOSE: This finds the farthest retreat point from an enemy and heads towards it
|
|
// ADDITIONS NEEDED: Try and make this take into account the player[0] tank
|
|
/////////////////////////////////
|
|
find_farthest_retreat_point( enemy )
|
|
{
|
|
best_dist = 0;
|
|
ref_pt = -1;
|
|
|
|
for( i = 0; i < level.retreat_reference_points.size; i++ )
|
|
{
|
|
dist = distanceSquared( level.retreat_reference_points[i].origin, enemy.origin );
|
|
if( dist > best_dist )
|
|
{
|
|
//-- check to see if its in the direction of the players tank
|
|
tank_dir = level.retreat_reference_points[i].origin - enemy.origin;
|
|
retreat_dir = enemy.origin - self.origin;
|
|
|
|
if( VectorDot( VectorNormalize( tank_dir ), VectorNormalize( retreat_dir )) > 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
best_dist = dist;
|
|
ref_pt = i;
|
|
}
|
|
}
|
|
if( ref_pt < 0 )
|
|
{
|
|
return undefined;
|
|
}
|
|
|
|
node = level.retreat_nodes[ref_pt][randomint(level.retreat_nodes[ref_pt].size)];
|
|
return node;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: build_retreat_generics
|
|
// CALLED ON: level
|
|
// PURPOSE: Sets up retreat points that will be used by infantry who have been flushed out
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
build_retreat_generics()
|
|
{
|
|
level.retreat_reference_points = [];
|
|
level.retreat_nodes = [];
|
|
|
|
for( i = 0;; i++ )
|
|
{
|
|
ref = getNode( "retreat "+i, "targetname" );
|
|
if( !isDefined( ref ) )
|
|
{
|
|
break;
|
|
}
|
|
level.retreat_reference_points = array_add( level.retreat_reference_points, ref );
|
|
level.retreat_nodes = array_add( level.retreat_nodes, GetNodeArray( "retreat "+i, "script_noteworthy" ) );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_bunker_infantry
|
|
// CALLED ON: an infantryman
|
|
// PURPOSE: Sets up basic AI behavior for german AI
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_bunker_infantry()
|
|
{
|
|
level.enemy_infantry = array_add( level.enemy_infantry, self );
|
|
self thread enforce_flame_deaths();
|
|
if( self.classname == "actor_axis_ger_ber_wehr_reg_panzerschrek" )
|
|
{
|
|
self thread do_antitank_AI();
|
|
//self thread unlimited_rocket_ammo();
|
|
self setThreatBiasGroup( "enemy antitank" );
|
|
self set_ent_see2_bias_group( "enemy antitank" );
|
|
self.a.rockets = 200;
|
|
self.maxsightdistsqrd = 3000*3000;
|
|
self.a.no_weapon_switch = true;
|
|
}
|
|
else
|
|
{
|
|
self setThreatBiasGroup( "enemy infantry" );
|
|
self set_ent_see2_bias_group( "enemy infantry" );
|
|
}
|
|
}
|
|
|
|
unlimited_rocket_ammo()
|
|
{
|
|
self endon("death");
|
|
|
|
while(1)
|
|
{
|
|
if( self.bulletsInClip < weaponClipSize( self.weapon ) )
|
|
{
|
|
self.bulletsInClip = weaponClipSize( self.weapon );
|
|
}
|
|
wait(0.5);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_generic_retreats
|
|
// CALLED ON: a flushed out infantryman
|
|
// PURPOSE: This makes them flee based on player proximity and seek the farthest retreat point
|
|
// they can find
|
|
// ADDITIONS NEEDED: Need to make this so they go to their retreat node and then just chill in the wheat.
|
|
/////////////////////////////////
|
|
do_generic_retreats()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( IsDefined(self.scripted_grenade_throw) )
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
for( i = 0; i < get_players().size; i++ )
|
|
{
|
|
if( distanceSquared( self.origin, get_players()[i].origin ) < 1000 * 1000 )
|
|
{
|
|
node = self find_farthest_retreat_point( get_players()[i] );
|
|
self.goal_radius = 64;
|
|
self setGoalNode( node );
|
|
level notify("ai_set_new_retreat_node");
|
|
|
|
self waittill("goal");
|
|
self.ignoreme = false;
|
|
self.ignoreall = false;
|
|
self AllowedStances( "crouch", "prone" );
|
|
return;
|
|
}
|
|
}
|
|
wait( 0.2 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_retreat_trucks
|
|
// CALLED ON: level
|
|
// PURPOSE: sets up all retreat truck behavior
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_retreat_trucks()
|
|
{
|
|
trucks = getEntArray( "retreat truck", "targetname" );
|
|
for( i = 0; i < trucks.size; i++ )
|
|
{
|
|
trucks[i] thread retreat_truck_behavior();
|
|
vehicle_node = GetVehicleNode( trucks[i].target, "targetname" );
|
|
if(IsDefined( vehicle_node ))
|
|
{
|
|
trucks[i] thread maps\_vehicle::vehicle_paths(vehicle_node);
|
|
}
|
|
wait( 2 );
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// EVENT 2
|
|
////////////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: radio_tower_begin
|
|
// CALLED ON: level
|
|
// PURPOSE: Sets up all level variables for starting at the radio tower objective
|
|
// ADDITIONS NEEDED: Needs to identify warp points for the player and warp them to an appropriate
|
|
// distance
|
|
/////////////////////////////////
|
|
radio_tower_begin()
|
|
{
|
|
flag_set( "flak 88s destroyed" );
|
|
|
|
thread do_sound_for_event( "battlechatter" );
|
|
|
|
level thread setup_early_tanks();
|
|
wait_for_first_player();
|
|
wait( 4 );
|
|
level thread setup_player_tanks();
|
|
|
|
wait( 1 );
|
|
new_starts = [];
|
|
for( i = 0; i < level.player_tanks.size; i++ )
|
|
{
|
|
start_pt = getStruct( "radio_tower start "+i, "targetname" );
|
|
if( isDefined( start_pt.target ) )
|
|
{
|
|
warp_pt = getStruct( start_pt.target, "targetname" );
|
|
}
|
|
else
|
|
{
|
|
warp_pt = start_pt;
|
|
}
|
|
if( isDefined( start_pt.script_noteworthy ) )
|
|
{
|
|
level.current_advance_level = start_pt.script_noteworthy;
|
|
}
|
|
level.player_tanks[i].origin = warp_pt.origin;
|
|
if( isDefined( level.player_tanks[i].current_node ) )
|
|
{
|
|
level.player_tanks[i].current_node = warp_pt;
|
|
}
|
|
if( isDefined( level.player_tanks[i].target_node ) )
|
|
{
|
|
level.player_tanks[i].target_node = warp_pt;
|
|
}
|
|
}
|
|
|
|
do_area_spawn( 1 );
|
|
wait_network_frame();
|
|
spawn_area_two_infantry();
|
|
|
|
level thread wait_for_group_spawn( 2 );
|
|
level thread wait_for_group_spawn( 3 );
|
|
|
|
//level thread wait_for_area_three_infantry();
|
|
//level thread wait_for_area_four_infantry();
|
|
level thread setup_airstrike_triggers();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_area_two_infantry
|
|
// CALLED ON: level
|
|
// PURPOSE: Waits until the area two spawn trigger has been hit then spawns all radio tower
|
|
// infantry
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_area_two_infantry()
|
|
{
|
|
trigger = GetEnt( "group 1 spawn", "script_noteworthy" );
|
|
trigger waittill( "trigger" );
|
|
|
|
if( flag( "radio tower destroyed" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
spawn_area_two_infantry();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: spawn_area_two_infantry
|
|
// CALLED ON: level
|
|
// PURPOSE: spawns all radio tower infantry and sets up their spawn functions
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
spawn_area_two_infantry()
|
|
{
|
|
spawner_array = GetEntArray( "radio tower spawner", "script_noteworthy" );
|
|
array_thread( spawner_array, ::add_spawn_function, ::setup_radio_tower_infantry );
|
|
array_thread( spawner_array, ::add_spawn_function, ::radio_tower_infantry_evasion );
|
|
array_thread( spawner_array, ::add_spawn_function, ::radio_tower_infantry_grenades );
|
|
|
|
spawner_array_2 = GetEntArray( "radio tower bunker spawner", "script_noteworthy" );
|
|
array_thread( spawner_array, ::add_spawn_function, ::setup_radio_tower_infantry );
|
|
|
|
|
|
flood_spawner_array = GetEntArray( "radio tower floodspawners", "script_noteworthy" );
|
|
array_thread( flood_spawner_array, ::add_spawn_function, ::radio_tower_infantry_grenades );
|
|
|
|
spawn_guys( spawner_array, undefined, true );
|
|
spawn_guys( spawner_array_2, undefined, true );
|
|
|
|
do_floodspawners( "radio tower", 1 );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: add_radio_tower_objective
|
|
// CALLED ON: level
|
|
// PURPOSE: This creates a series of breadcrumb target points that will be used to navigate
|
|
// the player to the radio tower and then updates the objective once the radio tower
|
|
// has been destroyed
|
|
// ADDITIONS NEEDED: None
|
|
//
|
|
//
|
|
// Glocke: Changed this so that the objective starts when the player hits a trigger where he can see the radio tower.
|
|
//
|
|
/////////////////////////////////
|
|
add_radio_tower_objective( radiotower )
|
|
{
|
|
level endon ( "radio tower start alternate" );
|
|
|
|
level waittill( "begin event 2" );
|
|
level notify( "radio tower start normal" );
|
|
|
|
proceed_trigger = GetEnt( "radio tower proceed trigger", "targetname" );
|
|
next_proceed_trigger = GetEnt( "radio tower next proceed trigger", "targetname" );
|
|
|
|
thread do_radio_tower_owned_event();
|
|
|
|
objective_add(2, "current" );
|
|
Objective_String( 2, &"SEE2_PROCEED_RADIOTOWER" );
|
|
|
|
Objective_additionalPosition( 2, 0, proceed_trigger.origin );
|
|
|
|
proceed_trigger waittill( "trigger" );
|
|
Objective_additionalPosition( 2, 0, next_proceed_trigger.origin );
|
|
|
|
next_proceed_trigger waittill( "trigger" );
|
|
|
|
Objective_String( 2, &"SEE2_DESTROY_RADIOTOWER" );
|
|
|
|
objective_additionalPosition( 2, 0, radiotower.origin );
|
|
|
|
while( 1 )
|
|
{
|
|
if( radiotower.model == "anim_seelow_radiotower_d" )
|
|
{
|
|
flag_set( "radio tower objective completed" );
|
|
Objective_State( 2, "done" );
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
add_radio_tower_objective_alternate( radiotower )
|
|
{
|
|
level endon( "radio tower start normal");
|
|
|
|
objective_start_tower = GetEnt( "radio tower next proceed trigger", "targetname" );
|
|
objective_start_tower waittill( "trigger" );
|
|
|
|
level notify( "radio tower start alternate" );
|
|
|
|
proceed_trigger = GetEnt( "radio tower proceed trigger", "targetname" );
|
|
next_proceed_trigger = GetEnt( "radio tower next proceed trigger", "targetname" );
|
|
|
|
thread do_radio_tower_owned_event();
|
|
|
|
objective_add(2, "current" );
|
|
Objective_String( 2, &"SEE2_DESTROY_RADIOTOWER" );
|
|
objective_additionalPosition( 2, 0, radiotower.origin );
|
|
|
|
while( 1 )
|
|
{
|
|
if( radiotower.model == "anim_seelow_radiotower_d" )
|
|
{
|
|
flag_set( "radio tower objective completed" );
|
|
Objective_State( 2, "done" );
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_radio_tower_owned_event
|
|
// CALLED ON: level
|
|
// PURPOSE: Causes a T34 to pull up at the bottleneck leading to the radio tower, engage in a
|
|
// firefight with a panther, then get owned
|
|
// ADDITIONS NEEDED: This can be triggered after the vehicles have already been set to move
|
|
// It needs to accommodate this either by restructuring the move trigger setup
|
|
// in radiant or through a multi-trigger system that checks for the flak 88s
|
|
// being destroyed before starting their move
|
|
/////////////////////////////////
|
|
do_radio_tower_owned_event()
|
|
{
|
|
vig_trigger = getEnt( "radio tower vig trigger", "script_noteworthy" );
|
|
|
|
vig_trigger waittill( "trigger" );
|
|
|
|
enemy_tank = undefined;
|
|
while(!IsDefined(enemy_tank))
|
|
{
|
|
enemy_tank = getEnt( "radio tower vig tank", "script_noteworthy" );
|
|
wait(0.05);
|
|
}
|
|
|
|
friendly_tank = undefined;
|
|
while(!IsDefined(friendly_tank))
|
|
{
|
|
friendly_tank = getEnt( "radio tower owned tank", "script_noteworthy" );
|
|
wait(0.05);
|
|
}
|
|
|
|
enemy_tank setTurretTargetEnt( friendly_tank );
|
|
friendly_tank setTurretTargetEnt( enemy_tank );
|
|
|
|
//enemy_tank thread keep_tank_alive();
|
|
//friendly_tank thread keep_tank_alive();
|
|
|
|
enemy_tank thread dummy_fire_behavior();
|
|
friendly_tank thread dummy_fire_behavior();
|
|
|
|
friendly_tank thread friendly_tank_wait_for_enemy_tank_to_die( enemy_tank );
|
|
enemy_tank thread enemy_tank_wait_for_friendly_tank_to_die( friendly_tank );
|
|
/*
|
|
friendly_tank setWaitNode( getVehicleNode( "radio tower owned node", "script_noteworthy" ) );
|
|
|
|
friendly_tank waittill( "reached_wait_node" );
|
|
|
|
friendly_tank.health = 1;
|
|
friendly_tank stop_keep_tank_alive();
|
|
wait( 0.05 );
|
|
enemy_tank stop_keep_tank_alive();
|
|
|
|
friendly_tank waittill( "death" );
|
|
|
|
enemy_tank.health = 1;
|
|
|
|
friendly_tank notify( "stop dummy firing" );
|
|
enemy_tank notify( "stop dummy firing" );
|
|
|
|
enemy_tank thread moving_firing_behavior();
|
|
*/
|
|
}
|
|
|
|
friendly_tank_wait_for_enemy_tank_to_die( enemy_tank )
|
|
{
|
|
self endon("death");
|
|
|
|
self thread friendly_tank_wait_for_enemy_tank_die_then_move( enemy_tank );
|
|
|
|
enemy_tank waittill("death");
|
|
|
|
self.enemy_tank_dead = true;
|
|
self notify( "stop dummy firing" );
|
|
self notify( "stop_burst_fire_unmanned" );
|
|
self notify( "stop_using_built_in_burst_fire" );
|
|
}
|
|
|
|
friendly_tank_wait_for_enemy_tank_die_then_move( enemy_tank )
|
|
{
|
|
self endon("death");
|
|
self waittill("pink_tank_wait_here");
|
|
self SetSpeed(0, 5, 5);
|
|
|
|
//enemy_tank waittill("death");
|
|
while(!IsDefined(self.enemy_tank_dead))
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
self ResumeSpeed(5);
|
|
self waittill("pink_tank_blow_up");
|
|
RadiusDamage( self.origin, 200, 10000, 9999 );
|
|
}
|
|
|
|
enemy_tank_wait_for_friendly_tank_to_die( friendly_tank )
|
|
{
|
|
self endon("death");
|
|
friendly_tank waittill("death");
|
|
|
|
self notify( "stop dummy firing" );
|
|
self thread moving_firing_behavior();
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: dummy_fire_behavior
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: Used to have the vignette vehicles from ::do_radio_tower_owned_event fire at each
|
|
// other
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
dummy_fire_behavior()
|
|
{
|
|
self endon( "stop dummy firing" );
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
wait( randomintrange( 2,5 ) );
|
|
self fireweapon();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_radio_tower_explode
|
|
// CALLED ON: level
|
|
// PURPOSE: Plays the fx for the radio tower explosion and animates it falling over
|
|
// ADDITIONS NEEDED: need to update the fx, either through script or fx editor to have a dust
|
|
// cloud play at the right time when the radio tower falls. Also, adding
|
|
// dead crushed nazis in the bunker the tower falls on would be sweet.
|
|
/////////////////////////////////
|
|
#using_animtree( "see2_models" );
|
|
do_radio_tower_explode()
|
|
{
|
|
|
|
radiotower = getEnt( "radio tower", "script_noteworthy" );
|
|
flag_init( radiotower.script_noteworthy+" destroyed" );
|
|
thread check_for_tower_damage( radiotower, 1500 );
|
|
level thread add_radio_tower_objective( radiotower );
|
|
level thread add_radio_tower_objective_alternate( radiotower );
|
|
|
|
level flag_wait( radiotower.script_noteworthy+" destroyed" );
|
|
|
|
playfx( level._effect["tower_explode"], radiotower.origin );
|
|
radiotower setModel( "anim_seelow_radiotower_d" );
|
|
radiotower.animname = "radiotower";
|
|
radiotower SetAnimTree();
|
|
|
|
wait( 4 );
|
|
|
|
radiotower anim_single_solo( radiotower, "fall" );
|
|
playfx( level._effect["tower_secondary_explosion"], radiotower.origin );
|
|
//level thread move_radio_tower_blocker( true );
|
|
|
|
autosave_by_name( "radio tower destroyed" );
|
|
|
|
level thread fuel_depot_objectives();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_tower_damage
|
|
// CALLED ON: level
|
|
// PURPOSE: keeps track of how much damage has been done to the tower
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_for_tower_damage( tower, amt )
|
|
{
|
|
count = 0;
|
|
damage_trigger = GetEnt( tower.script_noteworthy+" damage trigger", "script_noteworthy" );
|
|
while( 1 )
|
|
{
|
|
damage_trigger waittill( "damage", damage, other, direction, origin, damage_type );
|
|
if( array_check_for_dupes( get_players(), other ) || !explosive_damage( damage_type ) )
|
|
{
|
|
continue;
|
|
}
|
|
count += damage;
|
|
if( count >= amt )
|
|
{
|
|
flag_set( tower.script_noteworthy+" destroyed" );
|
|
return;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_radio_tower_infantry
|
|
// CALLED ON: level
|
|
// PURPOSE: sets up basic AI values for radio tower infantry behavior
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_radio_tower_infantry()
|
|
{
|
|
self endon( "death" );
|
|
|
|
level.enemy_infantry = array_add( level.enemy_infantry, self );
|
|
self thread enforce_flame_deaths();
|
|
if( self.classname == "actor_axis_ger_ber_wehr_reg_panzerschrek" )
|
|
{
|
|
self setThreatBiasGroup( "enemy antitank" );
|
|
self thread do_antitank_AI();
|
|
self set_ent_see2_bias_group( "enemy antitank" );
|
|
self.a.rockets = 200;
|
|
self.maxsightdistsqrd = 3000*3000;
|
|
self.a.no_weapon_switch = true;
|
|
self.goalradius = 128;
|
|
}
|
|
else
|
|
{
|
|
self setThreatBiasGroup( "enemy infantry" );
|
|
self set_ent_see2_bias_group( "enemy infantry" );
|
|
self.goalradius = 128;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// EVENT 3
|
|
////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: fuel_depot_begin
|
|
// CALLED ON: level
|
|
// PURPOSE: This sets up all the variables to start at the "Rejoin the Main Russian Army event" of
|
|
// see2
|
|
// ADDITIONS NEEDED: Need to add a warp position and warp the player and tank to this position
|
|
/////////////////////////////////
|
|
fuel_depot_begin()
|
|
{
|
|
flag_set( "flak 88s destroyed" );
|
|
flag_set( "radio tower destroyed" );
|
|
|
|
thread do_sound_for_event( "battlechatter" );
|
|
|
|
level thread setup_early_tanks();
|
|
wait_for_first_player();
|
|
wait( 4 );
|
|
level thread setup_player_tanks();
|
|
|
|
do_area_spawn( 2 );
|
|
wait_network_frame();
|
|
|
|
spawn_area_three_infantry();
|
|
|
|
level thread wait_for_group_spawn( 3 );
|
|
|
|
level thread wait_for_area_four_infantry();
|
|
level thread setup_airstrike_triggers();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: fuel_depot_objectives
|
|
// CALLED ON: level
|
|
// PURPOSE: Creates an objective star where the airstrike will be triggered and cleans up once
|
|
// the trigger has been hit.
|
|
// ADDITIONS NEEDED: None
|
|
////////////////////////////////
|
|
fuel_depot_objectives()
|
|
{
|
|
if( flag( "final battle already started" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
start_trigger = getEnt( "finalbattle_trigger", "script_noteworthy" );
|
|
|
|
objective_add(3, "current" );
|
|
Objective_String( 3, &"SEE2_REJOIN_ARMY" );
|
|
Objective_additionalPosition( 3, 0, start_trigger.origin );
|
|
|
|
//start_trigger = getEnt( "finalbattle_trigger", "script_noteworthy" );
|
|
while( 1 )
|
|
{
|
|
start_trigger waittill( "trigger", guy );
|
|
if( isPlayer( guy ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
autosave_by_name( "fuel supplies destroyed" );
|
|
|
|
level thread wait_for_finalbattle();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_finalbattle_alternate()
|
|
// CALLED ON: level
|
|
// PURPOSE: If the player bypasses all of the objectives and ends up at the ridge, then he gets put on the last objective
|
|
//
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_finalbattle_alternate()
|
|
{
|
|
start_trigger = GetEnt( "finalbattle_trigger", "script_noteworthy" );
|
|
start_trigger waittill( "trigger" );
|
|
|
|
wait(0.2);
|
|
|
|
if( !flag( "final battle already started" ) )
|
|
{
|
|
level thread wait_for_finalbattle( true );
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_tiger_retreats
|
|
// CALLED ON: level
|
|
// PURPOSE: Gets two tigers on the hill to retreat down the hill, leading the player to the final
|
|
// battle
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_tiger_retreats()
|
|
{
|
|
for( i = 1; ; i++ )
|
|
{
|
|
retreat_tiger = GetEnt( "retreat tiger "+i, "script_noteworthy" );
|
|
retreat_endpoint = getVehicleNode( "end retreat tiger "+i, "script_noteworthy" );
|
|
if( !isDefined( retreat_tiger ) || !isDefined( retreat_endpoint ) )
|
|
{
|
|
break;
|
|
}
|
|
retreat_tiger setWaitNode( retreat_endpoint );
|
|
retreat_tiger thread wait_for_retreat_done();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_retreat_done
|
|
// CALLED ON: a retreating tiger
|
|
// PURPOSE: Waits until they reach the final node in their path then deletes them
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_retreat_done()
|
|
{
|
|
self waittill( "reached_wait_node" );
|
|
|
|
self delete();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_area_three_infantry
|
|
// CALLED ON: level
|
|
// PURPOSE: waits until the area three spawn trigger is hit then causes area three infantry to
|
|
// spawn.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_area_three_infantry()
|
|
{
|
|
trigger = GetEnt( "group 2 spawn", "script_noteworthy" );
|
|
|
|
//GLOCKE: TODO - find out why this trigger is missing
|
|
if(!IsDefined(trigger))
|
|
{
|
|
return;
|
|
}
|
|
|
|
trigger waittill( "trigger" );
|
|
|
|
if( flag( "fuel depot cleared" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
spawn_area_three_infantry();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: spawn_area_three_infantry
|
|
// CALLED ON: level
|
|
// PURPOSE: Sets up spawn functions for area three infantry then spawns them
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
spawn_area_three_infantry()
|
|
{
|
|
spawner_array = GetEntArray( "fuel depot spawner", "script_noteworthy" );
|
|
array_thread( spawner_array, ::add_spawn_function, ::setup_fuel_depot_infantry );
|
|
array_thread( spawner_array, ::add_spawn_function, ::fuel_depot_infantry_evasion );
|
|
|
|
spawn_guys( spawner_array, undefined, true );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_fuel_depot_infantry
|
|
// CALLED ON: an infantryman
|
|
// PURPOSE: Sets up AI values and routines for area 3 infantry
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_fuel_depot_infantry()
|
|
{
|
|
self endon( "death" );
|
|
|
|
level.enemy_infantry = array_add( level.enemy_infantry, self );
|
|
self thread enforce_flame_deaths();
|
|
if( self.classname == "actor_axis_ger_ber_wehr_reg_panzerschrek" )
|
|
{
|
|
self setThreatBiasGroup( "enemy antitank" );
|
|
//self thread unlimited_rocket_ammo();
|
|
self set_ent_see2_bias_group( "enemy antitank" );
|
|
self.a.rockets = 200;
|
|
self.maxsightdistsqrd = 3000*3000;
|
|
self.a.no_weapon_switch = true;
|
|
self.goalradius = 128;
|
|
self thread do_antitank_AI();
|
|
}
|
|
else
|
|
{
|
|
self setThreatBiasGroup( "enemy infantry" );
|
|
self set_ent_see2_bias_group( "enemy infantry" );
|
|
self.goalradius = 128;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////
|
|
// EVENT 4
|
|
//////////////////////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: air_strike_begin
|
|
// CALLED ON: level
|
|
// PURPOSE: Starts at the air strike just prior to the final battle. Sets up all variables to
|
|
// clear out old events
|
|
// ADDITIONS NEEDED: Add warp points to warp player and tank to the appropriate area of the map
|
|
/////////////////////////////////
|
|
air_strike_begin()
|
|
{
|
|
|
|
flag_set( "flak 88s destroyed" );
|
|
flag_set( "radio tower destroyed" );
|
|
flag_set( "fuel depot cleared" );
|
|
|
|
thread do_sound_for_event( "battlechatter" );
|
|
|
|
level thread setup_early_tanks();
|
|
wait_for_first_player();
|
|
wait( 4 );
|
|
thread setup_player_tanks();
|
|
wait(5);
|
|
do_victory_scene( true );
|
|
//do_area_spawn( 3 );
|
|
//wait_network_frame();
|
|
//spawn_area_four_infantry();
|
|
|
|
//level thread setup_airstrike_triggers();
|
|
|
|
|
|
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_area_four_infantry
|
|
// CALLED ON: level
|
|
// PURPOSE: waits until the area 4 spawn trigger is hit then spawns the area 4 infantry
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_area_four_infantry()
|
|
{
|
|
trigger = GetEnt( "group 3 spawn", "script_noteworthy" );
|
|
trigger waittill( "trigger" );
|
|
|
|
if( flag( "final line breached" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
spawn_area_four_infantry();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: spawn_area_four_infantry
|
|
// CALLED ON: level
|
|
// PURPOSE: spawns all the area 4 infantry and sends them to fortify forward positions
|
|
// ADDITIONS NEEDED: Need to create a "setup_airstrike_infantry" function that sets up any
|
|
// unique AI behavior.
|
|
/////////////////////////////////
|
|
spawn_area_four_infantry()
|
|
{
|
|
spawner_array = GetEntArray( "final battle spawner", "script_noteworthy" );
|
|
|
|
spawn_guys( spawner_array, undefined, true );
|
|
/*
|
|
trigger_array = GetEntArray( "fourth area trigger", "targetname" );
|
|
|
|
for( i = 0; i < trigger_array.size; i++ )
|
|
{
|
|
trigger_array[i] notify( "trigger" );
|
|
wait_network_frame();
|
|
}
|
|
wait( 1 );
|
|
*/
|
|
//do_final_battle_fortify();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_final_battle_fortify
|
|
// CALLED ON: level
|
|
// PURPOSE: Sets up the mg_turrets in this area to fire at the massed russian drones
|
|
// ADDITIONS NEEDED:
|
|
/////////////////////////////////
|
|
do_final_battle_fortify()
|
|
{
|
|
mg_array = getEntArray( "holdout mg", "script_noteworthy" );
|
|
for( i = 0; i < mg_array.size; i++ )
|
|
{
|
|
mg_array[i].script_fireondrones = 1;
|
|
mg_array[i] setturretignoregoals( true );
|
|
mg_array[i] thread maps\_mgturret::mg42_target_drones( undefined, "axis", undefined );
|
|
}
|
|
/*
|
|
num_fort_positions = 7;
|
|
for( i = 1; i < num_fort_positions; i++ )
|
|
{
|
|
node = GetNode( "fortify_node"+i, "script_noteworthy" );
|
|
soldiers = get_living_ai_array( "fortify soldier "+i, "script_noteworthy" );
|
|
for( j = 0; j < soldiers.size; j++ )
|
|
{
|
|
if( isDefined( node ) && isDefined( soldiers[j] ) )
|
|
{
|
|
soldiers[j].goalradius = 512;
|
|
soldiers[j] setGoalNode( node );
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_airstrike_triggers
|
|
// CALLED ON: level
|
|
// PURPOSE: Waits for all airstrike triggers.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_airstrike_triggers()
|
|
{
|
|
airstrike_triggers = GetEntArray( "airstrike_trigger", "script_noteworthy" );
|
|
|
|
for( i = 0; i < airstrike_triggers.size; i++ )
|
|
{
|
|
airstrike_triggers[i] thread wait_for_airstrike();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_airstrike
|
|
// CALLED ON: an airstrike trigger
|
|
// PURPOSE: Waits until the trigger is hit then tells a plane to fire its rockets
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_airstrike()
|
|
{
|
|
level.see2_max_tank_target_dist = 6000;
|
|
level.see2_max_tank_firing_dist = 5000;
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", ent );
|
|
|
|
if( flag( "final line breached" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ent notify( "fire rockets" );
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_airstrike_planes
|
|
// CALLED ON: level
|
|
// PURPOSE: Attaches all rocket and bomb models on airstrike planes
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_airstrike_planes()
|
|
{
|
|
rocket_planes = GetEntArray( "rocket plane", "script_noteworthy" );
|
|
level.rocket_planes = rocket_planes;
|
|
|
|
for( i = 0; i < rocket_planes.size; i++ )
|
|
{
|
|
rocket_planes[i].rockets = [];
|
|
rocket_planes[i].rocket_tags = [];
|
|
for( j = 1; j < 3; j++ )
|
|
{
|
|
left_tag = "tag_smallbomb0"+j+"Left";
|
|
right_tag = "tag_smallbomb0"+j+"Right";
|
|
shreck = spawn("script_model", rocket_planes[i] getTagOrigin( left_tag ) );
|
|
shreck.angles = rocket_planes[i] getTagAngles( left_tag );
|
|
shreck setmodel("katyusha_rocket");
|
|
shreck linkto( rocket_planes[i], left_tag );
|
|
rocket_planes[i].rockets = array_add( rocket_planes[i].rockets, shreck );
|
|
rocket_planes[i].rocket_tags = array_add( rocket_planes[i].rocket_tags, left_tag );
|
|
wait_network_frame();
|
|
shreck = spawn("script_model", rocket_planes[i] getTagOrigin( right_tag ) );
|
|
shreck.angles = rocket_planes[i] getTagAngles( right_tag );
|
|
shreck setmodel("katyusha_rocket");
|
|
shreck linkto( rocket_planes[i], right_tag );
|
|
rocket_planes[i].rockets = array_add( rocket_planes[i].rockets, shreck );
|
|
rocket_planes[i].rocket_tags = array_add( rocket_planes[i].rocket_tags, right_tag );
|
|
wait_network_frame();
|
|
}
|
|
|
|
for( z = 0; z < rocket_planes[i].rockets.size; z++ )
|
|
{
|
|
rocket_planes[i].rockets[z] Hide();
|
|
}
|
|
|
|
rocket_planes[i] thread wait_for_fire_rockets();
|
|
}
|
|
|
|
level thread run_airstrike_planes();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: run_airstrike_planes
|
|
// CALLED ON: level
|
|
// PURPOSE: Sets all the airstrike planes off in order, script controlled for easier tweaking
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
run_airstrike_planes()
|
|
{
|
|
start_move_triggers = [];
|
|
start_move_triggers = GetEntArray( "rocket_plane_starts", "targetname");
|
|
|
|
start_trigger = getEnt( "finalbattle_trigger", "script_noteworthy" );
|
|
start_trigger waittill( "trigger" );
|
|
|
|
for(i =0; i < level.rocket_planes.size; i++)
|
|
{
|
|
for( z = 0; z < level.rocket_planes[i].rockets.size; z++ )
|
|
{
|
|
level.rocket_planes[i].rockets[z] Hide();
|
|
}
|
|
level.rocket_planes[i] thread DeleteMeAtEndOfPath();
|
|
}
|
|
|
|
player = get_players()[0];
|
|
for(i = 0; i < start_move_triggers.size; i++ )
|
|
{
|
|
start_move_triggers[i] UseBy( player );
|
|
//Kevins plane audio
|
|
level.rocket_planes[i] thread bomber_planes();
|
|
wait(1.5);
|
|
}
|
|
}
|
|
//Kevins audio for planes
|
|
bomber_planes()
|
|
{
|
|
while(distance(self.origin,GetPlayers()[0].origin) > 10000)
|
|
wait(.01);
|
|
self playsound ("fly_by");
|
|
}
|
|
|
|
DeleteMeAtEndOfPath()
|
|
{
|
|
self waittill( "reached_end_node" );
|
|
self Delete();
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_fire_rockets
|
|
// CALLED ON: an airstrike plane
|
|
// PURPOSE: waits until the "fire rockets" signal is given then fires the rockets from this plane
|
|
// at the target points laid out in radiant for it
|
|
// ADDITIONS NEEDED: Maybe make them acquire targets rather than firing at static points
|
|
/////////////////////////////////
|
|
wait_for_fire_rockets()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill( "fire rockets" );
|
|
targetnodes = [];
|
|
for( i = 1; i <= self.rockets.size; i++ )
|
|
{
|
|
targetnodes = array_add( targetnodes, getStruct( self.targetname+" target node "+i, "script_noteworthy" ) );
|
|
}
|
|
for( j = 0; j < self.rockets.size; j++ )
|
|
{
|
|
self.rockets[j] thread fire_rocket_at_pos( targetnodes[j] );
|
|
wait( randomintrange( 1, 2 ) );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: fire_rocket_at_pos
|
|
// CALLED ON: a rocket
|
|
// PURPOSE: Moves a rocket to it's target position and blows it up when it arrives.
|
|
// ADDITIONS NEEDED: Damage tweaking
|
|
/////////////////////////////////
|
|
fire_rocket_at_pos( target_struct )
|
|
{
|
|
start_pos = self.origin;
|
|
end_pos = target_struct.origin;
|
|
angles = vectortoangles( end_pos - start_pos );
|
|
self.angles = angles;
|
|
distance = distance( start_pos, end_pos );
|
|
time = distance/4200;
|
|
|
|
self unlink();
|
|
self moveTo( end_pos, time );
|
|
playFxOnTag( level._effect["pc132_trail"], self, "tag_fx" );
|
|
self playloopsound("rpg_rocket");
|
|
wait time;
|
|
|
|
self hide();
|
|
|
|
playfx( level._effect["pc132_explode"], self.origin );
|
|
|
|
self stoploopsound();
|
|
playSoundAtPosition("rpg_impact_boom", self.origin);
|
|
radiusdamage( self.origin, 256, 250, 250 );
|
|
earthquake( 0.5, 1.5, self.origin, 512 );
|
|
|
|
wait( 4 );
|
|
self delete();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_finalbattle
|
|
// CALLED ON: level
|
|
// PURPOSE: Waits until the player breaches the final trigger then plays the end cutscene
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_finalbattle( skipped_radio )
|
|
{
|
|
flag_set( "final battle already started" );
|
|
|
|
wait( 1 );
|
|
|
|
tanks = getEntArray( "obj tanks", "targetname" );
|
|
|
|
if( !IsDefined( skipped_radio ) )
|
|
{
|
|
Objective_State( 3, "done" );
|
|
}
|
|
|
|
Objective_add( 4, "current" );
|
|
Objective_String( 4, &"SEE2_BREAK_THE_LINE" );
|
|
|
|
autosave_by_name( "line broken" );
|
|
|
|
level thread do_victory_scene();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_victory_scene
|
|
// CALLED ON: level
|
|
// PURPOSE: plays the end IGC
|
|
// ADDITIONS NEEDED: Need to more adequately clean up remaining enemy AI, the attempts to do
|
|
// radius damage to enemy vehicles and directly kill infantry seems not to be
|
|
// working.
|
|
//
|
|
//
|
|
// Glocke: added in the objective check so that if the 88s and radio tower aren't killed, then the level fails them
|
|
// didn't properly handle co-op or splitscreen, fixed that (made it so that the scene always runs off of the host)
|
|
//
|
|
//
|
|
// players = get_players();
|
|
//
|
|
// level.otherPlayersSpectate = true;
|
|
// level.otherPlayersSpectateClient = players[0];
|
|
//
|
|
// for( i = 1; i < players.size; i++ )
|
|
// {
|
|
//
|
|
// players[i] thread maps\_callbackglobal::spawnSpectator();
|
|
// }
|
|
//
|
|
/////////////////////////////////
|
|
do_victory_scene( jumpto )
|
|
{
|
|
if(!IsDefined(jumpto))
|
|
{
|
|
victory_trigger = GetEnt( "victory trigger", "targetname" );
|
|
obj_trigger = GetEnt( "special obj trigger", "targetname" );
|
|
|
|
Objective_additionalPosition( 4, 0, obj_trigger.origin );
|
|
guy = undefined;
|
|
|
|
while( 1 )
|
|
{
|
|
victory_trigger waittill( "trigger", guy );
|
|
if( !isPlayer( guy ) )
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
level.nextmission_cleanup = ::clean_up_fadeout_hud;
|
|
level notify( "kill the audio queue" ); //-- kill the audio queue... this should help some of the noise problems
|
|
|
|
if( !flag( "flak objective completed" ) )
|
|
{
|
|
Objective_state( 1, "failed" );
|
|
}
|
|
|
|
if( !flag( "radio tower objective completed" ) )
|
|
{
|
|
Objective_state( 2, "failed" );
|
|
}
|
|
|
|
Objective_state( 4, "done" );
|
|
|
|
flag_clear( "battlechatter allowed" );
|
|
|
|
players = get_players();
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
players[i] thread magic_bullet_shield();
|
|
if(!IsDefined(jumpto))
|
|
{
|
|
players[i] FreezeControls( true );
|
|
}
|
|
}
|
|
|
|
//-- moved all of this to kill the enemies sooner
|
|
for( i = 0; i < level.enemy_armor.size; i++ )
|
|
{
|
|
if( isDefined( level.enemy_armor[i] ) && isDefined( level.enemy_armor[i].health ) )
|
|
{
|
|
level.enemy_armor[i].health = 1;
|
|
radiusDamage( level.enemy_armor[i].origin, 1000, 10, 10 );
|
|
}
|
|
}
|
|
|
|
level.enemy_infantry = GetAIArray( "axis" );
|
|
for( i = 0; i < level.enemy_infantry.size; i++ )
|
|
{
|
|
if( isDefined( level.enemy_infantry[i] ) && isDefined( level.enemy_infantry[i].health ) )
|
|
{
|
|
//level.enemy_infantry[i] doDamage( level.enemy_infantry[i].health * 2, level.enemy_infantry[i].origin );
|
|
level.enemy_infantry[i] thread bloody_death( 0.2 );
|
|
}
|
|
}
|
|
|
|
wait(0.75);
|
|
|
|
//-- Playing the commisar dialogue off of the first player
|
|
players[0].myTank playSound( level.scr_sound["commissar"]["victory1"], "commissar done" );
|
|
players[0].myTank waittill_notify_or_timeout("commissar done", 10);
|
|
|
|
for(i=0; i < players.size; i++)
|
|
{
|
|
players[i] SetClientDvar( "hud_showStance", "0" );
|
|
players[i] SetClientDvar( "miniscoreboardhide", 1 );
|
|
}
|
|
level thread fade_to_black( 2 );
|
|
|
|
players[0].myTank playSound( level.scr_sound["commissar"]["victory2"], "commissar done" );
|
|
players[0].myTank waittill_notify_or_timeout("commissar done", 10);
|
|
players[0].myTank playSound( level.scr_sound["commissar"]["victory3"], "commissar done" );
|
|
players[0].myTank waittill_notify_or_timeout("commissar done", 10);
|
|
|
|
share_screen( get_host(), true, true );
|
|
|
|
wait( 1 );
|
|
|
|
//Glocke: Pull everyone from their tanks so that their weapons can be disabled
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
players[i].myTank MakeVehicleUsable();
|
|
players[i].myTank UseBy( players[i] );
|
|
wait(0.05);
|
|
players[i].myTank MakeVehicleUnusable();
|
|
}
|
|
|
|
/*
|
|
player_tank = guy.myTank;
|
|
player_tank makevehicleusable();
|
|
player_tank useby( guy );
|
|
wait( 0.05 );
|
|
player_tank makevehicleunusable();
|
|
*/
|
|
|
|
wait( 0.05 );
|
|
|
|
link = getent( "player_temp_ending_pos", "script_noteworthy" );
|
|
anim_node = getent( "temp_center", "targetname" );
|
|
|
|
players[0] TakeWeapon( "m2_flamethrower" );
|
|
|
|
for( i = 0; i < players.size; i++ )
|
|
{
|
|
players[i] hide();
|
|
players[i] setorigin( link.origin + (0,0,4) );
|
|
players[i] setplayerangles( link.angles );
|
|
players[i] notify( "stop damage hud" );
|
|
players[i] disableWeapons();
|
|
|
|
if( i != 0)
|
|
{
|
|
//-- put the player in spectator mode
|
|
level.otherPlayersSpectate = true;
|
|
level.otherPlayersSpectateClient = players[0];
|
|
|
|
players[i] thread maps\_callbackglobal::spawnSpectator();
|
|
}
|
|
}
|
|
|
|
//Glocke: Make this the host only
|
|
//for( i = 0; i < players.size; i++ )
|
|
//{
|
|
//Kevins notify to start audio for outro
|
|
level notify("walla");
|
|
level thread maps\see2_anim::play_player_anim_outro( 0, players[0], anim_node );
|
|
//}
|
|
level thread play_center_car_anims_side();
|
|
level thread play_center_car_anims_middle();
|
|
|
|
while( !flag( "player_ready_for_outro" ) || !flag( "outro_group_1_ready" ) || !flag( "outro_group_2_ready" ) )
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
|
|
level thread fade_from_black( 2 );
|
|
//iprintlnbold( "OUTRO: Player get on the train." );
|
|
wait( 34 );
|
|
//kevin fading audio
|
|
level notify("audio_fade");
|
|
|
|
level thread fade_to_black(2);
|
|
wait(2);
|
|
for(i=0; i < players.size; i++)
|
|
{
|
|
players[i] SetClientDvar( "miniscoreboardhide", 0 );
|
|
}
|
|
|
|
thread nextmission();
|
|
wait(0.5);
|
|
share_screen( get_host(), false, false );
|
|
}
|
|
|
|
clean_up_fadeout_hud()
|
|
{
|
|
if(IsDefined(level.fadetoblack))
|
|
{
|
|
level.fadetoblack Destroy();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: play_center_car_anims_side
|
|
// CALLED ON: level
|
|
// PURPOSE: spawns center car side guys and plays the center car side anims
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
play_center_car_anims_side()
|
|
{
|
|
//anim_node = getnode( "outro_center_origin", "targetname" );
|
|
anim_node = getent( "temp_center", "targetname" );
|
|
|
|
guys = [];
|
|
|
|
left_spawn_points = getstructarray( "center_car_guy_left", "targetname" );
|
|
|
|
//guys[0] = spawn_fake_guy_outro( left_spawn_points[0].origin, left_spawn_points[0].angles, "guyl1", "chernov" );
|
|
guys[0] = spawn_fake_guy_outro( left_spawn_points[1].origin, left_spawn_points[1].angles, "guyl2" );
|
|
guys[1] = spawn_fake_guy_outro( left_spawn_points[2].origin, left_spawn_points[2].angles, "guyl3" );
|
|
guys[2] = spawn_fake_guy_outro( left_spawn_points[3].origin, left_spawn_points[3].angles, "guyl4" );
|
|
// This is chernov 4
|
|
//guys[1] = spawn_fake_guy_outro( left_spawn_points[4].origin, left_spawn_points[4].angles, "guyl5" );
|
|
guys[3] = spawn_fake_hero_outro( left_spawn_points[4].origin, left_spawn_points[4].angles, "guyl5", "chernov" );
|
|
|
|
//guys[3] = spawn_fake_guy_outro( left_spawn_points[5].origin, left_spawn_points[5].angles, "guyl6" );
|
|
|
|
right_spawn_points = getstructarray( "center_car_guy_left", "targetname" );
|
|
|
|
guys[4] = spawn_fake_guy_outro( right_spawn_points[0].origin, right_spawn_points[0].angles, "guyr1" );
|
|
guys[5] = spawn_fake_guy_outro( right_spawn_points[1].origin, right_spawn_points[1].angles, "guyr2" );
|
|
|
|
flag_set( "outro_group_1_ready" );
|
|
|
|
while( !flag( "player_ready_for_outro" ) || !flag( "outro_group_1_ready" ) || !flag( "outro_group_2_ready" ) )
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
anim_node anim_single( guys, "outro" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: play_center_car_anims_middle
|
|
// CALLED ON: level
|
|
// PURPOSE: spawns center car middle guys and playes the center car middle anims
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
#using_animtree( "generic_human" );
|
|
play_center_car_anims_middle() // ( R1-6 M1-6 L7-12 )
|
|
{
|
|
anim_node = getent( "temp_center", "targetname" );
|
|
|
|
guys = [];
|
|
|
|
center_spawn_points = getstructarray( "center_car_guy_center", "targetname" );
|
|
|
|
guys[0] = spawn_fake_hero_outro( center_spawn_points[0].origin, center_spawn_points[0].angles, "guyc1", "chernov" );
|
|
guys[1] = spawn_fake_guy_outro( center_spawn_points[1].origin, center_spawn_points[1].angles, "guyc2" );
|
|
// reznov is 2
|
|
//guys[2] = spawn_fake_guy_outro( center_spawn_points[2].origin, center_spawn_points[2].angles, "guyc3" );
|
|
guys[2] = spawn_fake_hero_outro( center_spawn_points[2].origin, center_spawn_points[2].angles, "guyc3", "reznov" );
|
|
|
|
guys[3] = spawn_fake_guy_outro( center_spawn_points[3].origin, center_spawn_points[3].angles, "guyc4" );
|
|
|
|
guys[4] = spawn_fake_guy_outro( center_spawn_points[4].origin, center_spawn_points[4].angles, "guyc5" );
|
|
guys[5] = spawn_fake_guy_outro( center_spawn_points[5].origin, center_spawn_points[5].angles, "guyc6" );
|
|
|
|
flag_set( "outro_group_2_ready" );
|
|
|
|
while( !flag( "player_ready_for_outro" ) || !flag( "outro_group_1_ready" ) || !flag( "outro_group_2_ready" ) )
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
anim_node anim_single( guys, "outro" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: spawn_fake_guy_outro
|
|
// CALLED ON: level
|
|
// PURPOSE: spawns a drone for the level outro
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
spawn_fake_guy_outro( startpoint, startangles, anim_name )
|
|
{
|
|
while(!OkToSpawn())
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
guy = spawn( "script_model", startpoint );
|
|
guy.angles = startangles;
|
|
|
|
guy character\char_rus_r_rifle::main();
|
|
guy maps\_drones::drone_allies_assignWeapon_russian();
|
|
guy attach( "weapon_rus_mosinnagant_rifle", "tag_weapon_right" );
|
|
|
|
|
|
guy.team = "allies";
|
|
guy.for_ai_print = 1;
|
|
guy.a = guy;
|
|
|
|
guy UseAnimTree( #animtree );
|
|
guy.animname = anim_name;
|
|
guy makeFakeAI();
|
|
guy.health = 100;
|
|
|
|
return guy;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: spawn_fake_hero_outro
|
|
// CALLED ON: level
|
|
// PURPOSE: This spawns a chernov or reznov model for use in the outro
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
spawn_fake_hero_outro( startpoint, startangles, anim_name, heroname )
|
|
{
|
|
guy = spawn( "script_model", startpoint );
|
|
guy.angles = startangles;
|
|
|
|
if( heroname == "reznov" )
|
|
{
|
|
guy character\char_rus_h_reznov_coat::main();
|
|
}
|
|
else
|
|
{
|
|
guy character\char_rus_p_chernova::main();
|
|
guy attach( "weapon_rus_mosinnagant_rifle", "tag_weapon_right");
|
|
}
|
|
guy maps\_drones::drone_allies_assignWeapon_russian();
|
|
|
|
guy.team = "allies";
|
|
guy.a = guy;
|
|
|
|
guy UseAnimTree( #animtree );
|
|
guy.animname = anim_name;
|
|
guy makeFakeAI();
|
|
guy.health = 100;
|
|
|
|
return guy;
|
|
|
|
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: fade_to_black
|
|
// CALLED ON: level
|
|
// PURPOSE: fades down to black in fadeTime
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
fade_to_black( fadeTime )
|
|
{
|
|
if( !IsDefined( fadeTime ) )
|
|
{
|
|
fadeTime = 5;
|
|
}
|
|
|
|
// fade to black
|
|
level.fadetoblack = NewHudElem();
|
|
level.fadetoblack.x = 0;
|
|
level.fadetoblack.y = 0;
|
|
level.fadetoblack.alpha = 0;
|
|
|
|
level.fadetoblack.horzAlign = "fullscreen";
|
|
level.fadetoblack.vertAlign = "fullscreen";
|
|
level.fadetoblack.foreground = false;
|
|
level.fadetoblack.sort = 50;
|
|
level.fadetoblack SetShader( "black", 640, 480 );
|
|
|
|
// Fade into black
|
|
level.fadetoblack FadeOverTime( fadeTime );
|
|
level.fadetoblack.alpha = 1;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION:fade_from_black
|
|
// CALLED ON: level
|
|
// PURPOSE: fades up from black in fadeTime
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
fade_from_black( fadeTime )
|
|
{
|
|
if( !IsDefined( fadeTime ) )
|
|
{
|
|
fadeTime = 5;
|
|
}
|
|
|
|
// Fade into black
|
|
level.fadetoblack FadeOverTime( fadeTime );
|
|
level.fadetoblack.alpha = 0;
|
|
}
|
|
|
|
//////////////////////////////////
|
|
// END SEE2.GSC
|
|
/////////////////////////////////
|
|
|
|
|
|
debug_ai_prints()
|
|
{
|
|
/#
|
|
if( GetDvar( "debug_ai_print" ) == "" )
|
|
{
|
|
SetDvar( "debug_ai_print", "0" );
|
|
}
|
|
|
|
if( GetDvar( "debug_ai_print_range" ) == "" )
|
|
{
|
|
SetDvar( "debug_ai_print_range", "1000" );
|
|
}
|
|
|
|
level.debug_ai_print = false;
|
|
while( 1 )
|
|
{
|
|
wait( 0.5 );
|
|
|
|
if( GetDvar( "debug_ai_print" ) == "0" )
|
|
{
|
|
level.debug_ai_print = false;
|
|
continue;
|
|
}
|
|
|
|
level.debug_ai_print = true;
|
|
|
|
//ai = GetAiArray();
|
|
ai = GetEntArray( "script_model", "classname");
|
|
|
|
for( i = 0; i < ai.size; i++ )
|
|
{
|
|
if(IsDefined( ai[i].for_ai_print ) )
|
|
{
|
|
ai[i] thread debug_ai_prints_thread();
|
|
}
|
|
}
|
|
}
|
|
#/
|
|
}
|
|
|
|
debug_ai_prints_thread()
|
|
{
|
|
/#
|
|
self endon( "death" );
|
|
|
|
if( IsDefined( self.ai_print ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self.ai_print = true;
|
|
|
|
while( level.debug_ai_print )
|
|
{
|
|
range = GetDvarInt( "debug_ai_print_range" );
|
|
player = get_host();
|
|
if( DistanceSquared( player.origin, self.origin ) < range * range )
|
|
{
|
|
print3d( self.origin + ( 0, 0, 72 ), self get_debug_ai_print() );
|
|
wait( 0.05 );
|
|
}
|
|
else
|
|
{
|
|
wait( 0.2 );
|
|
}
|
|
}
|
|
|
|
self.ai_print = false;
|
|
#/
|
|
}
|
|
|
|
get_debug_ai_print()
|
|
{
|
|
dvar = GetDvar( "debug_ai_print" );
|
|
switch( dvar )
|
|
{
|
|
case "script_noteworthy":
|
|
value = self.script_noteworthy;
|
|
break;
|
|
case "threatbias":
|
|
value = self.threatbias;
|
|
break;
|
|
case "getthreatbiasgroup":
|
|
value = self GetThreatBiasGroup();
|
|
break;
|
|
case "accuracy":
|
|
value = self.accuracy;
|
|
break;
|
|
case "ignoreme":
|
|
value = self.ignoreme;
|
|
break;
|
|
case "ignoreall":
|
|
value = self.ignoreall;
|
|
break;
|
|
case "health":
|
|
value = self.health;
|
|
break;
|
|
case "goalradius":
|
|
value = self.goalradius;
|
|
break;
|
|
case "moveplaybackrate":
|
|
value = self.moveplaybackrate;
|
|
break;
|
|
case "animname":
|
|
value = self.animname;
|
|
break;
|
|
case "script_forcecolor":
|
|
if( IsDefined( self.script_forceColor ) )
|
|
{
|
|
value = self.script_forceColor;
|
|
}
|
|
else
|
|
{
|
|
value = "undefined";
|
|
}
|
|
break;
|
|
case "player_seek":
|
|
if( IsDefined( self.player_seek ) )
|
|
{
|
|
value = self.player_seek;
|
|
}
|
|
else
|
|
{
|
|
value = "undefined";
|
|
}
|
|
break;
|
|
default:
|
|
value = "undefined";
|
|
}
|
|
|
|
if( !IsDefined( value ) )
|
|
{
|
|
value = "no animname";
|
|
}
|
|
|
|
return value;
|
|
}
|