/*************************************************
 * Decompiled by Serious and Edited by SyndiShanX
 * Script: mp\_vehicle.gsc
*************************************************/

#using scripts\codescripts\struct;
#using scripts\mp\_util;
#using scripts\mp\gametypes\_spawning;
#using scripts\mp\killstreaks\_qrdrone;
#using scripts\mp\killstreaks\_rcbomb;
#using scripts\shared\array_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\hud_util_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#using scripts\shared\vehicle_death_shared;
#using_animtree("mp_vehicles");
#namespace vehicle;

function autoexec __init__sytem__() {
  system::register("vehicle", &__init__, undefined, undefined);
}

function __init__() {
  setDvar("scr_veh_cleanupdebugprint", "0");
  setDvar("scr_veh_driversarehidden", "1");
  setDvar("scr_veh_driversareinvulnerable", "1");
  setDvar("scr_veh_alive_cleanuptimemin", "119");
  setDvar("scr_veh_alive_cleanuptimemax", "120");
  setDvar("scr_veh_dead_cleanuptimemin", "20");
  setDvar("scr_veh_dead_cleanuptimemax", "30");
  setDvar("scr_veh_cleanuptime_dmgfactor_min", "0.33");
  setDvar("scr_veh_cleanuptime_dmgfactor_max", "1.0");
  setDvar("scr_veh_cleanuptime_dmgfactor_deadtread", "0.25");
  setDvar("scr_veh_cleanuptime_dmgfraction_curve_begin", "0.0");
  setDvar("scr_veh_cleanuptime_dmgfraction_curve_end", "1.0");
  setDvar("scr_veh_cleanupabandoned", "1");
  setDvar("scr_veh_cleanupdrifted", "1");
  setDvar("scr_veh_cleanupmaxspeedmph", "1");
  setDvar("scr_veh_cleanupmindistancefeet", "75");
  setDvar("scr_veh_waittillstoppedandmindist_maxtime", "10");
  setDvar("scr_veh_waittillstoppedandmindist_maxtimeenabledistfeet", "5");
  setDvar("scr_veh_respawnafterhuskcleanup", "1");
  setDvar("scr_veh_respawntimemin", "50");
  setDvar("scr_veh_respawntimemax", "90");
  setDvar("scr_veh_respawnwait_maxiterations", "30");
  setDvar("scr_veh_respawnwait_iterationwaitseconds", "1");
  setDvar("scr_veh_disablerespawn", "0");
  setDvar("scr_veh_disableoverturndamage", "0");
  setDvar("scr_veh_explosion_spawnfx", "1");
  setDvar("scr_veh_explosion_doradiusdamage", "1");
  setDvar("scr_veh_explosion_radius", "256");
  setDvar("scr_veh_explosion_mindamage", "20");
  setDvar("scr_veh_explosion_maxdamage", "200");
  setDvar("scr_veh_ondeath_createhusk", "1");
  setDvar("scr_veh_ondeath_usevehicleashusk", "1");
  setDvar("scr_veh_explosion_husk_forcepointvariance", "30");
  setDvar("scr_veh_explosion_husk_horzvelocityvariance", "25");
  setDvar("scr_veh_explosion_husk_vertvelocitymin", "100");
  setDvar("scr_veh_explosion_husk_vertvelocitymax", "200");
  setDvar("scr_veh_explode_on_cleanup", "1");
  setDvar("scr_veh_disappear_maxwaittime", "60");
  setDvar("scr_veh_disappear_maxpreventdistancefeet", "30");
  setDvar("scr_veh_disappear_maxpreventvisibilityfeet", "150");
  setDvar("scr_veh_health_tank", "1350");
  level.vehicle_drivers_are_invulnerable = getdvarint("scr_veh_driversareinvulnerable");
  level.onejectoccupants = &vehicle_eject_all_occupants;
  level.vehiclehealths["panzer4_mp"] = 2600;
  level.vehiclehealths["t34_mp"] = 2600;
  setDvar("scr_veh_health_jeep", "700");
  if(init_vehicle_entities()) {
    level.vehicle_explosion_effect = "_t6/vehicle/vexplosion/fx_vexplode_helicopter_exp_mp";
    level.veh_husk_models = [];
    level.veh_husk_effects = [];
    if(isDefined(level.use_new_veh_husks)) {
      level.veh_husk_models["t34_mp"] = "veh_t34_destroyed_mp";
    }
    if(isDefined(level.onaddvehiclehusks)) {
      [[level.onaddvehiclehusks]]();
    }
  }
  chopper_player_get_on_gun = % mp_vehicles::int_huey_gunner_on;
  chopper_door_open = % mp_vehicles::v_huey_door_open;
  chopper_door_open_state = % mp_vehicles::v_huey_door_open_state;
  chopper_door_closed_state = % mp_vehicles::v_huey_door_close_state;
  killbrushes = getEntArray("water_killbrush", "targetname");
  foreach(brush in killbrushes) {
    brush thread water_killbrush_think();
  }
}

function water_killbrush_think() {
  for(;;) {
    self waittill("trigger", entity);
    if(isDefined(entity)) {
      if(isDefined(entity.targetname)) {
        if(entity.targetname == "rcbomb") {
          entity notify("rcbomb_shutdown");
        } else if(entity.targetname == "talon" && (!(isDefined(entity.dead) && entity.dead))) {
          entity notify("death");
        }
      }
      if(isDefined(entity.helitype) && entity.helitype == "qrdrone") {
        entity qrdrone::qrdrone_force_destroy();
      }
    }
  }
}

function initialize_vehicle_damage_effects_for_level() {
  k_mild_damage_index = 0;
  k_moderate_damage_index = 1;
  k_severe_damage_index = 2;
  k_total_damage_index = 3;
  k_mild_damage_health_percentage = 0.85;
  k_moderate_damage_health_percentage = 0.55;
  k_severe_damage_health_percentage = 0.35;
  k_total_damage_health_percentage = 0;
  level.k_mild_damage_health_percentage = k_mild_damage_health_percentage;
  level.k_moderate_damage_health_percentage = k_moderate_damage_health_percentage;
  level.k_severe_damage_health_percentage = k_severe_damage_health_percentage;
  level.k_total_damage_health_percentage = k_total_damage_health_percentage;
  level.vehicles_damage_states = [];
  level.vehicles_husk_effects = [];
  level.vehicles_damage_treadfx = [];
  vehicle_name = get_default_vehicle_name();
  level.vehicles_damage_states[vehicle_name] = [];
  level.vehicles_damage_treadfx[vehicle_name] = [];
  level.vehicles_damage_states[vehicle_name][k_mild_damage_index] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_mild_damage_index].health_percentage = k_mild_damage_health_percentage;
  level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array = [];
  level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0].damage_effect = "_t6/vehicle/vfire/fx_tank_sherman_smldr";
  level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0].sound_effect = undefined;
  level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0].vehicle_tag = "tag_origin";
  level.vehicles_damage_states[vehicle_name][k_moderate_damage_index] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].health_percentage = k_moderate_damage_health_percentage;
  level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array = [];
  level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0].damage_effect = "_t6/vehicle/vfire/fx_vfire_med_12";
  level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0].sound_effect = undefined;
  level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0].vehicle_tag = "tag_origin";
  level.vehicles_damage_states[vehicle_name][k_severe_damage_index] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_severe_damage_index].health_percentage = k_severe_damage_health_percentage;
  level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array = [];
  level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0].damage_effect = "_t6/vehicle/vfire/fx_vfire_sherman";
  level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0].sound_effect = undefined;
  level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0].vehicle_tag = "tag_origin";
  level.vehicles_damage_states[vehicle_name][k_total_damage_index] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_total_damage_index].health_percentage = k_total_damage_health_percentage;
  level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array = [];
  level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0] = spawnStruct();
  level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0].damage_effect = "_t6/vehicle/vexplosion/fx_vexplode_helicopter_exp_mp";
  level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0].sound_effect = "vehicle_explo";
  level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0].vehicle_tag = "tag_origin";
  default_husk_effects = spawnStruct();
  default_husk_effects.damage_effect = undefined;
  default_husk_effects.sound_effect = undefined;
  default_husk_effects.vehicle_tag = "tag_origin";
  level.vehicles_husk_effects[vehicle_name] = default_husk_effects;
}

function get_vehicle_name(vehicle) {
  name = "";
  if(isDefined(vehicle)) {
    if(isDefined(vehicle.vehicletype)) {
      name = vehicle.vehicletype;
    }
  }
  return name;
}

function get_default_vehicle_name() {
  return "defaultvehicle_mp";
}

function get_vehicle_name_key_for_damage_states(vehicle) {
  vehicle_name = get_vehicle_name(vehicle);
  if(!isDefined(level.vehicles_damage_states) || !isDefined(level.vehicles_damage_states[vehicle_name])) {
    vehicle_name = get_default_vehicle_name();
  }
  return vehicle_name;
}

function get_vehicle_damage_state_index_from_health_percentage(vehicle) {
  if(!isDefined(level.vehicles_damage_states)) {
    return -1;
  }
  damage_state_index = -1;
  vehicle_name = get_vehicle_name_key_for_damage_states();
  for(test_index = 0; test_index < level.vehicles_damage_states[vehicle_name].size; test_index++) {
    if(vehicle.current_health_percentage <= level.vehicles_damage_states[vehicle_name][test_index].health_percentage) {
      damage_state_index = test_index;
      continue;
    }
    break;
  }
  return damage_state_index;
}

function update_damage_effects(vehicle, attacker) {
  if(vehicle.initial_state.health > 0) {
    previous_damage_state_index = get_vehicle_damage_state_index_from_health_percentage(vehicle);
    vehicle.current_health_percentage = vehicle.health / vehicle.initial_state.health;
    current_damage_state_index = get_vehicle_damage_state_index_from_health_percentage(vehicle);
    if(previous_damage_state_index != current_damage_state_index) {
      vehicle notify("damage_state_changed");
      if(previous_damage_state_index < 0) {
        start_damage_state_index = 0;
      } else {
        start_damage_state_index = previous_damage_state_index + 1;
      }
      play_damage_state_effects(vehicle, start_damage_state_index, current_damage_state_index);
      if(vehicle.health <= 0) {
        vehicle kill_vehicle(attacker);
      }
    }
  }
}

function play_damage_state_effects(vehicle, start_damage_state_index, end_damage_state_index) {
  vehicle_name = get_vehicle_name_key_for_damage_states(vehicle);
  for(damage_state_index = start_damage_state_index; damage_state_index <= end_damage_state_index; damage_state_index++) {
    for(effect_index = 0; effect_index < level.vehicles_damage_states[vehicle_name][damage_state_index].effect_array.size; effect_index++) {
      effects = level.vehicles_damage_states[vehicle_name][damage_state_index].effect_array[effect_index];
      vehicle thread play_vehicle_effects(effects);
    }
  }
}

function play_vehicle_effects(effects, isdamagedtread) {
  self endon("delete");
  self endon("removed");
  if(!isDefined(isdamagedtread) || isdamagedtread == 0) {
    self endon("damage_state_changed");
  }
  if(isDefined(effects.sound_effect)) {
    self playSound(effects.sound_effect);
  }
  waittime = 0;
  if(isDefined(effects.damage_effect_loop_time)) {
    waittime = effects.damage_effect_loop_time;
  }
  while(waittime > 0) {
    if(isDefined(effects.damage_effect)) {
      playFXOnTag(effects.damage_effect, self, effects.vehicle_tag);
    }
    wait(waittime);
  }
}

function init_vehicle_entities() {
  vehicles = getEntArray("script_vehicle", "classname");
  array::thread_all(vehicles, &init_original_vehicle);
  if(isDefined(vehicles)) {
    return vehicles.size;
  }
  return 0;
}

function precache_vehicles() {}

function register_vehicle() {
  if(!isDefined(level.vehicles_list)) {
    level.vehicles_list = [];
  }
  level.vehicles_list[level.vehicles_list.size] = self;
}

function manage_vehicles() {
  if(!isDefined(level.vehicles_list)) {
    return 1;
  }
  max_vehicles = getmaxvehicles();
  newarray = [];
  for(i = 0; i < level.vehicles_list.size; i++) {
    if(isDefined(level.vehicles_list[i])) {
      newarray[newarray.size] = level.vehicles_list[i];
    }
  }
  level.vehicles_list = newarray;
  vehiclestodelete = (level.vehicles_list.size + 1) - max_vehicles;
  if(vehiclestodelete > 0) {
    newarray = [];
    for(i = 0; i < level.vehicles_list.size; i++) {
      vehicle = level.vehicles_list[i];
      if(vehiclestodelete > 0) {
        if(isDefined(vehicle.is_husk) && !isDefined(vehicle.permanentlyremoved)) {
          deleted = vehicle husk_do_cleanup();
          if(deleted) {
            vehiclestodelete--;
            continue;
          }
        }
      }
      newarray[newarray.size] = vehicle;
    }
    level.vehicles_list = newarray;
  }
  return level.vehicles_list.size < max_vehicles;
}

function init_vehicle() {
  self register_vehicle();
  if(isDefined(level.vehiclehealths) && isDefined(level.vehiclehealths[self.vehicletype])) {
    self.maxhealth = level.vehiclehealths[self.vehicletype];
  } else {
    self.maxhealth = getdvarint("scr_veh_health_tank");
    println(("" + self.vehicletype) + "");
  }
  self.health = self.maxhealth;
  self vehicle_record_initial_values();
  self init_vehicle_threads();
  system::wait_till("spawning");
}

function initialize_vehicle_damage_state_data() {
  if(self.initial_state.health > 0) {
    self.current_health_percentage = self.health / self.initial_state.health;
    self.previous_health_percentage = self.health / self.initial_state.health;
  } else {
    self.current_health_percentage = 1;
    self.previous_health_percentage = 1;
  }
}

function init_original_vehicle() {
  self.original_vehicle = 1;
  self init_vehicle();
}

function vehicle_wait_player_enter_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  while(true) {
    self waittill("enter_vehicle", player);
    player thread player_wait_exit_vehicle_t();
    player player_update_vehicle_hud(1, self);
  }
}

function player_wait_exit_vehicle_t() {
  self endon("disconnect");
  self waittill("exit_vehicle", vehicle);
  self player_update_vehicle_hud(0, vehicle);
}

function vehicle_wait_damage_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  while(true) {
    self waittill("damage");
    occupants = self getvehoccupants();
    if(isDefined(occupants)) {
      for(i = 0; i < occupants.size; i++) {
        occupants[i] player_update_vehicle_hud(1, self);
      }
    }
  }
}

function player_update_vehicle_hud(show, vehicle) {
  if(show) {
    if(!isDefined(self.vehiclehud)) {
      self.vehiclehud = hud::createbar((1, 1, 1), 64, 16);
      self.vehiclehud hud::setpoint("CENTER", "BOTTOM", 0, -40);
      self.vehiclehud.alpha = 0.75;
    }
    self.vehiclehud hud::updatebar(vehicle.health / vehicle.initial_state.health);
  } else if(isDefined(self.vehiclehud)) {
    self.vehiclehud hud::destroyelem();
  }
  if(getdvarstring("scr_vehicle_healthnumbers") != "") {
    if(getdvarint("scr_vehicle_healthnumbers") != 0) {
      if(show) {
        if(!isDefined(self.vehiclehudhealthnumbers)) {
          self.vehiclehudhealthnumbers = hud::createfontstring("default", 2);
          self.vehiclehudhealthnumbers hud::setparent(self.vehiclehud);
          self.vehiclehudhealthnumbers hud::setpoint("LEFT", "RIGHT", 8, 0);
          self.vehiclehudhealthnumbers.alpha = 0.75;
          self.vehiclehudhealthnumbers.hidewheninmenu = 0;
          self.vehiclehudhealthnumbers.archived = 0;
        }
        self.vehiclehudhealthnumbers setvalue(vehicle.health);
      } else if(isDefined(self.vehiclehudhealthnumbers)) {
        self.vehiclehudhealthnumbers hud::destroyelem();
      }
    }
  }
}

function init_vehicle_threads() {
  self thread vehicle_abandoned_by_drift_t();
  self thread vehicle_abandoned_by_occupants_t();
  self thread vehicle_damage_t();
  self thread vehicle_ghost_entering_occupants_t();
  self thread vehicle_recycle_spawner_t();
  self thread vehicle_disconnect_paths();
  if(isDefined(level.enablevehiclehealthbar) && level.enablevehiclehealthbar) {
    self thread vehicle_wait_player_enter_t();
    self thread vehicle_wait_damage_t();
  }
  self thread vehicle_wait_tread_damage();
  self thread vehicle_overturn_eject_occupants();
  if(getdvarint("scr_veh_disableoverturndamage") == 0) {
    self thread vehicle_overturn_suicide();
  }
  self thread cleanup_debug_print_t();
  self thread cleanup_debug_print_clearmsg_t();
}

function build_template(type, model, typeoverride) {
  if(isDefined(typeoverride)) {
    type = typeoverride;
  }
  if(!isDefined(level.vehicle_death_fx)) {
    level.vehicle_death_fx = [];
  }
  if(!isDefined(level.vehicle_death_fx[type])) {
    level.vehicle_death_fx[type] = [];
  }
  level.vehicle_compassicon[type] = 0;
  level.vehicle_team[type] = "axis";
  level.vehicle_life[type] = 999;
  level.vehicle_hasmainturret[model] = 0;
  level.vehicle_mainturrets[model] = [];
  level.vtmodel = model;
  level.vttype = type;
}

function build_rumble(rumble, scale, duration, radius, basetime, randomaditionaltime) {
  if(!isDefined(level.vehicle_rumble)) {
    level.vehicle_rumble = [];
  }
  struct = build_quake(scale, duration, radius, basetime, randomaditionaltime);
  assert(isDefined(rumble));
  struct.rumble = rumble;
  level.vehicle_rumble[level.vttype] = struct;
}

function build_quake(scale, duration, radius, basetime, randomaditionaltime) {
  struct = spawnStruct();
  struct.scale = scale;
  struct.duration = duration;
  struct.radius = radius;
  if(isDefined(basetime)) {
    struct.basetime = basetime;
  }
  if(isDefined(randomaditionaltime)) {
    struct.randomaditionaltime = randomaditionaltime;
  }
  return struct;
}

function build_exhaust(effect) {
  level.vehicle_exhaust[level.vtmodel] = effect;
}

function cleanup_debug_print_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  while(true) {
    if(isDefined(self.debug_message) && getdvarint("") != 0) {
      print3d(self.origin + vectorscale((0, 0, 1), 150), self.debug_message, (0, 1, 0), 1, 1, 1);
    }
    wait(0.01);
  }
}

function cleanup_debug_print_clearmsg_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  while(true) {
    self waittill("enter_vehicle");
    self.debug_message = undefined;
  }
}

function cleanup_debug_print(message) {
  self.debug_message = message;
}

function vehicle_abandoned_by_drift_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  self wait_then_cleanup_vehicle("Drift Test", "scr_veh_cleanupdrifted");
}

function vehicle_abandoned_by_occupants_timeout_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  self wait_then_cleanup_vehicle("Abandon Test", "scr_veh_cleanupabandoned");
}

function wait_then_cleanup_vehicle(test_name, cleanup_dvar_name) {
  self endon("enter_vehicle");
  self wait_until_severely_damaged();
  self do_alive_cleanup_wait(test_name);
  self wait_for_vehicle_to_stop_outside_min_radius();
  self cleanup(test_name, cleanup_dvar_name, &vehicle_recycle);
}

function wait_until_severely_damaged() {
  while(true) {
    health_percentage = self.health / self.initial_state.health;
    if(isDefined(level.k_severe_damage_health_percentage)) {
      self cleanup_debug_print(((("Damage Test: Still healthy - (") + health_percentage) + (" >= ") + level.k_severe_damage_health_percentage) + ") and working treads");
    } else {
      self cleanup_debug_print("Damage Test: Still healthy and working treads");
    }
    self waittill("damage");
    health_percentage = self.health / self.initial_state.health;
    if(isDefined(level.k_severe_damage_health_percentage) && health_percentage < level.k_severe_damage_health_percentage) {
      break;
    }
  }
}

function get_random_cleanup_wait_time(state) {
  varnameprefix = ("scr_veh_" + state) + "_cleanuptime";
  mintime = getdvarfloat(varnameprefix + "min");
  maxtime = getdvarfloat(varnameprefix + "max");
  if(maxtime > mintime) {
    return randomfloatrange(mintime, maxtime);
  }
  return maxtime;
}

function do_alive_cleanup_wait(test_name) {
  initialrandomwaitseconds = get_random_cleanup_wait_time("alive");
  secondswaited = 0;
  seconds_per_iteration = 1;
  while(true) {
    curve_begin = getdvarfloat("scr_veh_cleanuptime_dmgfraction_curve_begin");
    curve_end = getdvarfloat("scr_veh_cleanuptime_dmgfraction_curve_end");
    factor_min = getdvarfloat("scr_veh_cleanuptime_dmgfactor_min");
    factor_max = getdvarfloat("scr_veh_cleanuptime_dmgfactor_max");
    treaddeaddamagefactor = getdvarfloat("scr_veh_cleanuptime_dmgfactor_deadtread");
    damagefraction = 0;
    if(self is_vehicle()) {
      damagefraction = (self.initial_state.health - self.health) / self.initial_state.health;
    } else {
      damagefraction = 1;
    }
    damagefactor = 0;
    if(damagefraction <= curve_begin) {
      damagefactor = factor_max;
    } else {
      if(damagefraction >= curve_end) {
        damagefactor = factor_min;
      } else {
        dydx = (factor_min - factor_max) / (curve_end - curve_begin);
        damagefactor = factor_max + ((damagefraction - curve_begin) * dydx);
      }
    }
    totalsecstowait = initialrandomwaitseconds * damagefactor;
    if(secondswaited >= totalsecstowait) {
      break;
    }
    self cleanup_debug_print((test_name + ": Waiting ") + (totalsecstowait - secondswaited) + "s");
    wait(seconds_per_iteration);
    secondswaited = secondswaited + seconds_per_iteration;
  }
}

function do_dead_cleanup_wait(test_name) {
  total_secs_to_wait = get_random_cleanup_wait_time("dead");
  seconds_waited = 0;
  seconds_per_iteration = 1;
  while(seconds_waited < total_secs_to_wait) {
    self cleanup_debug_print((test_name + ": Waiting ") + (total_secs_to_wait - seconds_waited) + "s");
    wait(seconds_per_iteration);
    seconds_waited = seconds_waited + seconds_per_iteration;
  }
}

function cleanup(test_name, cleanup_dvar_name, cleanup_func) {
  keep_waiting = 1;
  while(keep_waiting) {
    cleanupenabled = !isDefined(cleanup_dvar_name) || getdvarint(cleanup_dvar_name) != 0;
    if(cleanupenabled != 0) {
      self[[cleanup_func]]();
      break;
    }
    keep_waiting = 0;
    self cleanup_debug_print(((("" + test_name) + "") + cleanup_dvar_name) + "");
    wait(5);
    keep_waiting = 1;
  }
}

function vehicle_wait_tread_damage() {
  self endon("death");
  self endon("delete");
  vehicle_name = get_vehicle_name(self);
  while(true) {
    self waittill("broken", brokennotify);
    if(brokennotify == "left_tread_destroyed") {
      if(isDefined(level.vehicles_damage_treadfx[vehicle_name]) && isDefined(level.vehicles_damage_treadfx[vehicle_name][0])) {
        self thread play_vehicle_effects(level.vehicles_damage_treadfx[vehicle_name][0], 1);
      }
    } else if(brokennotify == "right_tread_destroyed") {
      if(isDefined(level.vehicles_damage_treadfx[vehicle_name]) && isDefined(level.vehicles_damage_treadfx[vehicle_name][1])) {
        self thread play_vehicle_effects(level.vehicles_damage_treadfx[vehicle_name][1], 1);
      }
    }
  }
}

function wait_for_vehicle_to_stop_outside_min_radius() {
  maxwaittime = getdvarfloat("scr_veh_waittillstoppedandmindist_maxtime");
  iterationwaitseconds = 1;
  maxwaittimeenabledistinches = 12 * getdvarfloat("scr_veh_waittillstoppedandmindist_maxtimeenabledistfeet");
  initialorigin = self.initial_state.origin;
  totalsecondswaited = 0;
  while(totalsecondswaited < maxwaittime) {
    speedmph = self getspeedmph();
    cutoffmph = getdvarfloat("scr_veh_cleanupmaxspeedmph");
    if(speedmph > cutoffmph) {
      cleanup_debug_print(((("(" + (maxwaittime - totalsecondswaited)) + "s) Speed: ") + speedmph) + (">") + cutoffmph);
    } else {
      break;
    }
    wait(iterationwaitseconds);
    totalsecondswaited = totalsecondswaited + iterationwaitseconds;
  }
}

function vehicle_abandoned_by_occupants_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  while(true) {
    self waittill("exit_vehicle");
    occupants = self getvehoccupants();
    if(occupants.size == 0) {
      self play_start_stop_sound("tank_shutdown_sfx");
      self thread vehicle_abandoned_by_occupants_timeout_t();
    }
  }
}

function play_start_stop_sound(sound_alias, modulation) {
  self.start_stop_sfxid = self playSound(sound_alias);
}

function vehicle_ghost_entering_occupants_t() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  if(isDefined(self.vehicleclass) && "artillery" == self.vehicleclass) {
    return;
  }
  while(true) {
    self waittill("enter_vehicle", player, seat);
    isdriver = seat == 0;
    if(getdvarint("scr_veh_driversarehidden") != 0 && isdriver) {
      player ghost();
    }
    occupants = self getvehoccupants();
    if(occupants.size == 1) {
      self play_start_stop_sound("tank_startup_sfx");
    }
    player thread player_change_seat_handler_t(self);
    player thread player_leave_vehicle_cleanup_t(self);
  }
}

function player_is_occupant_invulnerable(smeansofdeath) {
  if(self isremotecontrolling()) {
    return 0;
  }
  if(!isDefined(level.vehicle_drivers_are_invulnerable)) {
    level.vehicle_drivers_are_invulnerable = 0;
  }
  invulnerable = level.vehicle_drivers_are_invulnerable && self player_is_driver();
  return invulnerable;
}

function player_is_driver() {
  if(!isalive(self)) {
    return false;
  }
  vehicle = self getvehicleoccupied();
  if(isDefined(vehicle)) {
    if(isDefined(vehicle.vehicleclass) && "artillery" == vehicle.vehicleclass) {
      return false;
    }
    seat = vehicle getoccupantseat(self);
    if(isDefined(seat) && seat == 0) {
      return true;
    }
  }
  return false;
}

function player_change_seat_handler_t(vehicle) {
  self endon("disconnect");
  self endon("exit_vehicle");
  while(true) {
    self waittill("change_seat", vehicle, oldseat, newseat);
    isdriver = newseat == 0;
    if(isdriver) {
      if(getdvarint("scr_veh_driversarehidden") != 0) {
        self ghost();
      }
    } else {
      self show();
    }
  }
}

function player_leave_vehicle_cleanup_t(vehicle) {
  self endon("disconnect");
  self waittill("exit_vehicle");
  currentweapon = self getcurrentweapon();
  if(isDefined(self.lastweapon) && self.lastweapon != currentweapon && self.lastweapon != level.weaponnone) {
    self switchtoweapon(self.lastweapon);
  }
  self show();
}

function vehicle_is_tank() {
  return self.vehicletype == "sherman_mp" || self.vehicletype == "panzer4_mp" || self.vehicletype == "type97_mp" || self.vehicletype == "t34_mp";
}

function vehicle_record_initial_values() {
  if(!isDefined(self.initial_state)) {
    self.initial_state = spawnStruct();
  }
  if(isDefined(self.origin)) {
    self.initial_state.origin = self.origin;
  }
  if(isDefined(self.angles)) {
    self.initial_state.angles = self.angles;
  }
  if(isDefined(self.health)) {
    self.initial_state.health = self.health;
  }
  self initialize_vehicle_damage_state_data();
}

function vehicle_should_explode_on_cleanup() {
  return getdvarint("scr_veh_explode_on_cleanup") != 0;
}

function vehicle_recycle() {
  self wait_for_unnoticeable_cleanup_opportunity();
  self.recycling = 1;
  self suicide();
}

function wait_for_vehicle_overturn() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  worldup = anglestoup(vectorscale((0, 1, 0), 90));
  overturned = 0;
  while(!overturned) {
    if(isDefined(self.angles)) {
      up = anglestoup(self.angles);
      dot = vectordot(up, worldup);
      if(dot <= 0) {
        overturned = 1;
      }
    }
    if(!overturned) {
      wait(1);
    }
  }
}

function vehicle_overturn_eject_occupants() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  for(;;) {
    self waittill("veh_ejectoccupants");
    if(isDefined(level.onejectoccupants)) {
      [[level.onejectoccupants]]();
    }
    wait(0.25);
  }
}

function vehicle_eject_all_occupants() {
  occupants = self getvehoccupants();
  if(isDefined(occupants)) {
    for(i = 0; i < occupants.size; i++) {
      if(isDefined(occupants[i])) {
        occupants[i] unlink();
      }
    }
  }
}

function vehicle_overturn_suicide() {
  self endon("transmute");
  self endon("death");
  self endon("delete");
  self wait_for_vehicle_overturn();
  seconds = randomfloatrange(5, 7);
  wait(seconds);
  damageorigin = self.origin + vectorscale((0, 0, 1), 25);
  self finishvehicleradiusdamage(self, self, 32000, 32000, 32000, 0, "MOD_EXPLOSIVE", level.weaponnone, damageorigin, 400, -1, (0, 0, 1), 0);
}

function suicide() {
  self kill_vehicle(self);
}

function kill_vehicle(attacker) {
  damageorigin = self.origin + (0, 0, 1);
  self finishvehicleradiusdamage(attacker, attacker, 32000, 32000, 10, 0, "MOD_EXPLOSIVE", level.weaponnone, damageorigin, 400, -1, (0, 0, 1), 0);
}

function value_with_default(preferred_value, default_value) {
  if(isDefined(preferred_value)) {
    return preferred_value;
  }
  return default_value;
}

function vehicle_transmute(attacker) {
  deathorigin = self.origin;
  deathangles = self.angles;
  vehicle_name = get_vehicle_name_key_for_damage_states(self);
  respawn_parameters = spawnStruct();
  respawn_parameters.origin = self.initial_state.origin;
  respawn_parameters.angles = self.initial_state.angles;
  respawn_parameters.health = self.initial_state.health;
  respawn_parameters.targetname = value_with_default(self.targetname, "");
  respawn_parameters.vehicletype = value_with_default(self.vehicletype, "");
  respawn_parameters.destructibledef = self.destructibledef;
  vehiclewasdestroyed = !isDefined(self.recycling);
  if(vehiclewasdestroyed || vehicle_should_explode_on_cleanup()) {
    _spawn_explosion(deathorigin);
    if(vehiclewasdestroyed && getdvarint("scr_veh_explosion_doradiusdamage") != 0) {
      explosionradius = getdvarint("scr_veh_explosion_radius");
      explosionmindamage = getdvarint("scr_veh_explosion_mindamage");
      explosionmaxdamage = getdvarint("scr_veh_explosion_maxdamage");
      self kill_vehicle(attacker);
      self radiusdamage(deathorigin, explosionradius, explosionmaxdamage, explosionmindamage, attacker, "MOD_EXPLOSIVE", getweapon(self.vehicletype + "_explosion"));
    }
  }
  self notify("transmute");
  respawn_vehicle_now = 1;
  if(vehiclewasdestroyed && getdvarint("scr_veh_ondeath_createhusk") != 0) {
    if(getdvarint("scr_veh_ondeath_usevehicleashusk") != 0) {
      husk = self;
      self.is_husk = 1;
    } else {
      husk = _spawn_husk(deathorigin, deathangles, self.vehmodel);
    }
    husk _init_husk(vehicle_name, respawn_parameters);
    if(getdvarint("scr_veh_respawnafterhuskcleanup") != 0) {
      respawn_vehicle_now = 0;
    }
  }
  if(!isDefined(self.is_husk)) {
    self remove_vehicle_from_world();
  }
  if(getdvarint("scr_veh_disablerespawn") != 0) {
    respawn_vehicle_now = 0;
  }
  if(respawn_vehicle_now) {
    respawn_vehicle(respawn_parameters);
  }
}

function respawn_vehicle(respawn_parameters) {
  mintime = getdvarint("scr_veh_respawntimemin");
  maxtime = getdvarint("scr_veh_respawntimemax");
  seconds = randomfloatrange(mintime, maxtime);
  wait(seconds);
  wait_until_vehicle_position_wont_telefrag(respawn_parameters.origin);
  if(!manage_vehicles()) {
    iprintln("");
  } else {
    if(isDefined(respawn_parameters.destructibledef)) {
      vehicle = spawnvehicle(respawn_parameters.vehicletype, respawn_parameters.origin, respawn_parameters.angles, respawn_parameters.targetname, respawn_parameters.destructibledef);
    } else {
      vehicle = spawnvehicle(respawn_parameters.vehicletype, respawn_parameters.origin, respawn_parameters.angles, respawn_parameters.targetname);
    }
    vehicle.vehicletype = respawn_parameters.vehicletype;
    vehicle.destructibledef = respawn_parameters.destructibledef;
    vehicle.health = respawn_parameters.health;
    vehicle init_vehicle();
    vehicle vehicle_telefrag_griefers_at_position(respawn_parameters.origin);
  }
}

function remove_vehicle_from_world() {
  self notify("removed");
  if(isDefined(self.original_vehicle)) {
    if(!isDefined(self.permanentlyremoved)) {
      self.permanentlyremoved = 1;
      self thread hide_vehicle();
    }
    return false;
  }
  self _delete_entity();
  return true;
}

function _delete_entity() {
  self delete();
}

function hide_vehicle() {
  under_the_world = (self.origin[0], self.origin[1], self.origin[2] - 10000);
  self.origin = under_the_world;
  wait(0.1);
  self hide();
  self notify("hidden_permanently");
}

function wait_for_unnoticeable_cleanup_opportunity() {
  maxpreventdistancefeet = getdvarint("scr_veh_disappear_maxpreventdistancefeet");
  maxpreventvisibilityfeet = getdvarint("scr_veh_disappear_maxpreventvisibilityfeet");
  maxpreventdistanceinchessq = (144 * maxpreventdistancefeet) * maxpreventdistancefeet;
  maxpreventvisibilityinchessq = (144 * maxpreventvisibilityfeet) * maxpreventvisibilityfeet;
  maxsecondstowait = getdvarfloat("scr_veh_disappear_maxwaittime");
  iterationwaitseconds = 1;
  secondswaited = 0;
  while(secondswaited < maxsecondstowait) {
    players_s = util::get_all_alive_players_s();
    oktocleanup = 1;
    for(j = 0; j < players_s.a.size && oktocleanup; j++) {
      player = players_s.a[j];
      distinchessq = distancesquared(self.origin, player.origin);
      if(distinchessq < maxpreventdistanceinchessq) {
        self cleanup_debug_print(((("(" + (maxsecondstowait - secondswaited)) + "s) Player too close: ") + distinchessq) + ("<") + maxpreventdistanceinchessq);
        oktocleanup = 0;
        continue;
      }
      if(distinchessq < maxpreventvisibilityinchessq) {
        vehiclevisibilityfromplayer = self sightconetrace(player.origin, player, anglesToForward(player.angles));
        if(vehiclevisibilityfromplayer > 0) {
          self cleanup_debug_print(("(" + (maxsecondstowait - secondswaited)) + "s) Player can see");
          oktocleanup = 0;
        }
      }
    }
    if(oktocleanup) {
      return;
    }
    wait(iterationwaitseconds);
    secondswaited = secondswaited + iterationwaitseconds;
  }
}

function wait_until_vehicle_position_wont_telefrag(position) {
  maxiterations = getdvarint("scr_veh_respawnwait_maxiterations");
  iterationwaitseconds = getdvarint("scr_veh_respawnwait_iterationwaitseconds");
  for(i = 0; i < maxiterations; i++) {
    if(!vehicle_position_will_telefrag(position)) {
      return;
    }
    wait(iterationwaitseconds);
  }
}

function vehicle_position_will_telefrag(position) {
  players_s = util::get_all_alive_players_s();
  for(i = 0; i < players_s.a.size; i++) {
    if(players_s.a[i] player_vehicle_position_will_telefrag(position)) {
      return true;
    }
  }
  return false;
}

function vehicle_telefrag_griefers_at_position(position) {
  attacker = self;
  inflictor = self;
  players_s = util::get_all_alive_players_s();
  for(i = 0; i < players_s.a.size; i++) {
    player = players_s.a[i];
    if(player player_vehicle_position_will_telefrag(position)) {
      player dodamage(20000, player.origin + (0, 0, 1), attacker, inflictor, "none");
    }
  }
}

function player_vehicle_position_will_telefrag(position) {
  distanceinches = 240;
  mindistinchessq = distanceinches * distanceinches;
  distinchessq = distancesquared(self.origin, position);
  return distinchessq < mindistinchessq;
}

function vehicle_recycle_spawner_t() {
  self endon("delete");
  self waittill("death", attacker);
  if(isDefined(self)) {
    self vehicle_transmute(attacker);
  }
}

function vehicle_play_explosion_sound() {
  self playSound("car_explo_large");
}

function vehicle_damage_t() {
  self endon("delete");
  self endon("removed");
  for(;;) {
    self waittill("damage", damage, attacker);
    players = getplayers();
    for(i = 0; i < players.size; i++) {
      if(!isalive(players[i])) {
        continue;
      }
      vehicle = players[i] getvehicleoccupied();
      if(isDefined(vehicle) && self == vehicle && players[i] player_is_driver()) {
        if(damage > 0) {
          earthquake(damage / 400, 1, players[i].origin, 512, players[i]);
        }
        if(damage > 100) {
          println("");
          players[i] playrumbleonentity("tank_damage_heavy_mp");
          continue;
        }
        if(damage > 10) {
          println("");
          players[i] playrumbleonentity("tank_damage_light_mp");
        }
      }
    }
    update_damage_effects(self, attacker);
  }
}

function _spawn_husk(origin, angles, modelname) {
  husk = spawn("script_model", origin);
  husk.angles = angles;
  husk setModel(modelname);
  husk.health = 1;
  husk setCanDamage(0);
  return husk;
}

function is_vehicle() {
  return isDefined(self.vehicletype);
}

function swap_to_husk_model() {
  if(isDefined(self.vehicletype)) {
    husk_model = level.veh_husk_models[self.vehicletype];
    if(isDefined(husk_model)) {
      self setModel(husk_model);
    }
  }
}

function _init_husk(vehicle_name, respawn_parameters) {
  self swap_to_husk_model();
  if(isDefined(level.vehicles_husk_effects)) {
    effects = level.vehicles_husk_effects[vehicle_name];
    self play_vehicle_effects(effects);
  }
  self.respawn_parameters = respawn_parameters;
  forcepointvariance = getdvarint("scr_veh_explosion_husk_forcepointvariance");
  horzvelocityvariance = getdvarint("scr_veh_explosion_husk_horzvelocityvariance");
  vertvelocitymin = getdvarint("scr_veh_explosion_husk_vertvelocitymin");
  vertvelocitymax = getdvarint("scr_veh_explosion_husk_vertvelocitymax");
  forcepointx = randomfloatrange(0 - forcepointvariance, forcepointvariance);
  forcepointy = randomfloatrange(0 - forcepointvariance, forcepointvariance);
  forcepoint = (forcepointx, forcepointy, 0);
  forcepoint = forcepoint + self.origin;
  initialvelocityx = randomfloatrange(0 - horzvelocityvariance, horzvelocityvariance);
  initialvelocityy = randomfloatrange(0 - horzvelocityvariance, horzvelocityvariance);
  initialvelocityz = randomfloatrange(vertvelocitymin, vertvelocitymax);
  initialvelocity = (initialvelocityx, initialvelocityy, initialvelocityz);
  if(self is_vehicle()) {
    self launchvehicle(initialvelocity, forcepoint);
  } else {
    self physicslaunch(forcepoint, initialvelocity);
  }
  self thread husk_cleanup_t();
  self thread cleanup_debug_print_t();
}

function husk_cleanup_t() {
  self endon("death");
  self endon("delete");
  self endon("hidden_permanently");
  respawn_parameters = self.respawn_parameters;
  self do_dead_cleanup_wait("Husk Cleanup Test");
  self wait_for_unnoticeable_cleanup_opportunity();
  self thread final_husk_cleanup_t(respawn_parameters);
}

function final_husk_cleanup_t(respawn_parameters) {
  self husk_do_cleanup();
  if(getdvarint("scr_veh_respawnafterhuskcleanup") != 0) {
    if(getdvarint("scr_veh_disablerespawn") == 0) {
      respawn_vehicle(respawn_parameters);
    }
  }
}

function husk_do_cleanup() {
  self _spawn_explosion(self.origin);
  if(self is_vehicle()) {
    return self remove_vehicle_from_world();
  }
  self _delete_entity();
  return 1;
}

function _spawn_explosion(origin) {
  if(getdvarint("scr_veh_explosion_spawnfx") == 0) {
    return;
  }
  if(isDefined(level.vehicle_explosion_effect)) {
    forward = (0, 0, 1);
    rot = randomfloat(360);
    up = (cos(rot), sin(rot), 0);
    playFX(level.vehicle_explosion_effect, origin, forward, up);
  }
  thread _play_sound_in_space("vehicle_explo", origin);
}

function _play_sound_in_space(soundeffectname, origin) {
  org = spawn("script_origin", origin);
  org.origin = origin;
  org playsoundwithnotify(soundeffectname, "sounddone");
  org waittill("sounddone");
  org delete();
}

function vehicle_kill_disconnect_paths_forever() {
  self notify("kill_disconnect_paths_forever");
}

function vehicle_disconnect_paths() {}

function follow_path(node) {
  self endon("death");
  assert(isDefined(node), "");
  self notify("newpath");
  if(isDefined(node)) {
    self.attachedpath = node;
  }
  pathstart = self.attachedpath;
  self.currentnode = self.attachedpath;
  if(!isDefined(pathstart)) {
    return;
  }
  self attachpath(pathstart);
  self startpath();
  self endon("newpath");
  nextpoint = pathstart;
  while(isDefined(nextpoint)) {
    self waittill("reached_node", nextpoint);
    self.currentnode = nextpoint;
    nextpoint notify("trigger", self);
    if(isDefined(nextpoint.script_noteworthy)) {
      self notify(nextpoint.script_noteworthy);
      self notify("noteworthy", nextpoint.script_noteworthy, nextpoint);
    }
    waittillframeend();
  }
}

function initvehiclemap() {
  root = "devgui_cmd \"MP/Vehicles/";
  adddebugcommand(root + "Spawn siegebot\" \"set scr_spawnvehicle 1\"\n");
  adddebugcommand(root + "Spawn siegebot boss\" \"set scr_spawnvehicle 2\"\n");
  adddebugcommand(root + "Spawn quadtank\" \"set scr_spawnvehicle 3\"\n");
  adddebugcommand(root + "Spawn mechtank\" \"set scr_spawnvehicle 4\"\n");
  thread vehiclemainthread();
}

function vehiclemainthread() {
  if(level.disablevehiclespawners === 1) {
    return;
  }
  spawn_nodes = struct::get_array("veh_spawn_point", "targetname");
  veh_spawner_id = 0;
  for(i = 0; i < spawn_nodes.size; i++) {
    spawn_node = spawn_nodes[i];
    veh_name = spawn_node.script_noteworthy;
    time_interval = int(spawn_node.script_parameters);
    if(!isDefined(veh_name)) {
      continue;
    }
    veh_spawner_id++;
    thread vehiclespawnthread(veh_spawner_id, veh_name, spawn_node.origin, spawn_node.angles, time_interval);
    if(isDefined(level.vehicle_spawner_init)) {
      level[[level.vehicle_spawner_init]](veh_spawner_id, veh_name, spawn_node.origin, spawn_node.angles);
    }
    wait(0.05);
  }
  if(isDefined(level.vehicle_spawners_init_finished)) {
    level thread[[level.vehicle_spawners_init_finished]]();
  }
}

function vehiclespawnthread(veh_spawner_id, veh_name, origin, angles, time_interval) {
  level endon("game_ended");
  veh_spawner = getent(veh_name + "_spawner", "targetname");
  kill_trigger = spawn("trigger_radius", origin, 0, 60, 180);
  level thread function_87e9a4ad(veh_name, origin, angles);
  var_45b6c208 = time_interval;
  while(true) {
    vehicle = veh_spawner spawnfromspawner(veh_name, 1, 1, 1);
    if(!isDefined(vehicle)) {
      wait(randomfloatrange(1, 2));
      continue;
    }
    if(isDefined(vehicle.archetype)) {
      vehicle asmrequestsubstate("locomotion@movement");
    }
    wait(0.05);
    vehicle.origin = origin;
    vehicle.angles = angles;
    vehicle.veh_spawner_id = veh_spawner_id;
    vehicle thread vehicleteamthread();
    level thread function_4b28749d(vehicle);
    vehicle waittill("death");
    vehicle vehicle_death::deletewhensafe(0.25);
    if(isDefined(level.vehicle_destroyed)) {
      level thread[[level.vehicle_destroyed]](veh_spawner_id);
    }
    time_interval = var_45b6c208;
    if(getdvarfloat("", 0) != 0) {
      time_interval = getdvarfloat("", 0);
      if(time_interval < 5.1) {
        time_interval = 5.1;
      }
    }
    if(isDefined(time_interval)) {
      level thread performvehicleprespawn(veh_spawner_id, veh_name, origin, angles, time_interval, kill_trigger);
      wait(time_interval);
    }
  }
}

function performvehicleprespawn(veh_spawner_id, veh_name, origin, angles, spawn_delay, kill_trigger) {
  fx_prespawn_time = 5;
  fx_spawn_delay = spawn_delay - fx_prespawn_time;
  wait(fx_spawn_delay);
  if(isDefined(level.vehicle_about_to_spawn)) {
    level thread[[level.vehicle_about_to_spawn]](veh_spawner_id, veh_name, origin, angles, fx_prespawn_time);
  }
  kill_overlap_time = 0.1;
  wait_before_kill = fx_prespawn_time - kill_overlap_time;
  wait(wait_before_kill);
  kill_duration_ms = (kill_overlap_time * 2) * 1000;
  level thread kill_any_touching(kill_trigger, kill_duration_ms);
  wait(kill_overlap_time);
}

function kill_any_touching(kill_trigger, kill_duration_ms) {
  kill_expire_time_ms = gettime() + kill_duration_ms;
  kill_weapon = getweapon("hero_minigun");
  while(gettime() <= kill_expire_time_ms) {
    foreach(player in level.players) {
      if(!isDefined(player)) {
        continue;
      }
      if(player istouching(kill_trigger)) {
        if(player isinvehicle()) {
          vehicle = player getvehicleoccupied();
          if(isDefined(vehicle) && vehicle.is_oob_kill_target === 1) {
            destroy_vehicle(vehicle);
            continue;
          }
        }
        player dodamage(player.health + 1, player.origin, kill_trigger, kill_trigger, "none", "MOD_SUICIDE", 0, kill_weapon);
      }
    }
    potential_victims = getaiarray();
    if(isDefined(potential_victims)) {
      foreach(entity in potential_victims) {
        if(!isDefined(entity)) {
          continue;
        }
        if(!entity istouching(kill_trigger)) {
          continue;
        }
        if(isDefined(entity.health) && entity.health <= 0) {
          continue;
        }
        if(isvehicle(entity)) {
          destroy_vehicle(entity);
        }
      }
    }
    wait(0.05);
  }
}

function destroy_vehicle(vehicle) {
  vehicle dodamage(vehicle.health + 10000, vehicle.origin, undefined, undefined, "none", "MOD_TRIGGER_HURT");
}

function function_87e9a4ad(veh_name, origin, angles) {
  fx_prespawn_time = 5;
  while(true) {
    if(getdvarint("", 0) == 0) {
      wait(1);
    } else {
      if(isDefined(level.vehicle_about_to_spawn)) {
        level thread[[level.vehicle_about_to_spawn]](veh_name, origin, angles, fx_prespawn_time);
      }
      wait(6);
    }
  }
}

function function_4b28749d(vehicle) {
  vehicle endon("death");
  setDvar("", 0);
  while(true) {
    if(getdvarint("") != 0) {
      destroy_vehicle(vehicle);
    }
    wait(1);
  }
}

function vehicleteamthread() {
  vehicle = self;
  vehicle endon("death");
  vehicle makevehicleusable();
  if(target_istarget(vehicle)) {
    target_remove(vehicle);
  }
  vehicle.nojumping = 1;
  vehicle.forcedamagefeedback = 1;
  vehicle.vehkilloccupantsondeath = 1;
  vehicle disableaimassist();
  while(true) {
    vehicle setteam("neutral");
    vehicle.ignoreme = 1;
    vehicle clientfield::set("toggle_lights", 1);
    if(target_istarget(vehicle)) {
      target_remove(vehicle);
    }
    vehicle waittill("enter_vehicle", player);
    player clearandcacheperks();
    vehicle setteam(player.team);
    vehicle.ignoreme = 0;
    vehicle clientfield::set("toggle_lights", 0);
    vehicle spawning::create_entity_enemy_influencer("small_vehicle", player.team);
    player spawning::enable_influencers(0);
    if(!target_istarget(vehicle)) {
      if(isDefined(vehicle.targetoffset)) {
        target_set(vehicle, vehicle.targetoffset);
      } else {
        target_set(vehicle, (0, 0, 0));
      }
    }
    vehicle thread watchplayerexitrequestthread(player);
    vehicle waittill("exit_vehicle", player);
    if(isDefined(player)) {
      player setcachedperks();
      player spawning::enable_influencers(1);
    }
    vehicle spawning::remove_influencers();
  }
}

function watchplayerexitrequestthread(player) {
  level endon("game_ended");
  player endon("death");
  player endon("disconnect");
  vehicle = self;
  vehicle endon("death");
  wait(1.5);
  while(true) {
    timeused = 0;
    while(player useButtonPressed()) {
      timeused = timeused + 0.05;
      if(timeused > 0.25) {
        player unlink();
        return;
      }
      wait(0.05);
    }
    wait(0.05);
  }
}

function clearandcacheperks() {
  self.perks_before_vehicle = self getperks();
  self clearperks();
}

function setcachedperks() {
  assert(isDefined(self.perks_before_vehicle));
  foreach(perk in self.perks_before_vehicle) {
    self setperk(perk);
  }
}