// hud_update_lock_indicator() will manage the non-rendering dependant part of // missle locking void hud_update_lock_indicator(float frametime) { ship_weapon *swp; weapon_info *wip; vector lock_world_pos; #ifndef NO_NETWORK // if i'm a multiplayer observer, bail here if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){ return; } #endif Assert(Player_ai->target_objnum != -1); // be sure to unset this flag, then possibly set later in this function so that // threat indicators work properly. Player_ai->ai_flags &= ~AIF_SEEK_LOCK; if ( hud_abort_lock() ) { hud_lock_reset(); return; } // if there is an EMP effect active, never update lock if(emp_active_local()){ hud_lock_reset(); return; } swp = &Player_ship->weapons; wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; Lock_start_dist = wip->min_lock_time * wip->lock_pixels_per_sec; // if secondary weapons change, reset the lock if ( hud_lock_secondary_weapon_changed(swp) ) { hud_lock_reset(); } Player_ai->last_secondary_index = swp->current_secondary_bank; if ( !(wip->wi_flags & WIF_HOMING_ASPECT) ) { hud_lock_reset(); return; } // Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags) if ( (Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (Objects[Player_ai->target_objnum].type != OBJ_WEAPON) ) { hud_lock_reset(); return; } hud_lock_determine_lock_point(&lock_world_pos); if ( !hud_lock_has_homing_point() ) { Player->target_in_lock_cone=0; } hud_lock_check_if_target_in_lock_cone(&lock_world_pos); // check if the target is within range of the current secondary weapon. If it is not, // a lock will not be detected if ( !hud_lock_target_in_range() ) { Player->target_in_lock_cone = 0; } // If locking on a subsystem, and not in sight... can't lock // Changed by MK on 4/3/98. It was confusing me that my hornets would not lock on my target. // It will now be confusing that they lock, but don't home on your subsystem, but I think that's preferable. // Often you really care about destroying the target, not just the subsystem. /*if ( Player_ai->targeted_subsys ) { if ( !hud_lock_on_subsys_ok() ) { Player->target_in_lock_cone=0; } }*/ if ( !Player->target_in_lock_cone ) { Player->locking_on_center=0; Player->locking_subsys_parent=-1; Player->locking_subsys=NULL; } hud_calculate_lock_position(frametime); if (!Players[Player_num].lock_indicator_visible) return; if (Player_ai->current_target_is_locked) { if ( Missile_track_loop > -1 ) { snd_chg_loop_status(Missile_track_loop, 0); Missile_track_loop = -1; Missile_lock_loop = snd_play(&Snds[SND_MISSILE_LOCK]); } } else { Player_ai->ai_flags |= AIF_SEEK_LOCK; // set this flag so multiplayer's properly track lock on other ships if ( Missile_lock_loop != -1 && snd_is_playing(Missile_lock_loop) ) { snd_stop(Missile_lock_loop); Missile_lock_loop = -1; } } }
// hud_calculate_lock_position() will determine where on the screen to draw the lock // indicator, and will determine when a lock has occurred. If the lock indicator is not // on the screen yet, hud_calculate_lock_start_pos() is called to pick a starting location void hud_calculate_lock_position(float frametime) { ship_weapon *swp; weapon_info *wip; static float pixels_moved_while_locking; static float pixels_moved_while_degrading; static int Need_new_start_pos = 0; static double accumulated_x_pixels, accumulated_y_pixels; double int_portion; static float last_dist_to_target; static int catching_up; static int maintain_lock_count = 0; static float catch_up_distance = 0.0f; double hypotenuse, delta_x, delta_y; swp = &Player_ship->weapons; wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; if (Player->target_in_lock_cone) { if (!Players[Player_num].lock_indicator_visible) { hud_calculate_lock_start_pos(); last_dist_to_target = 0.0f; Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x; Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y; Players[Player_num].lock_indicator_visible = 1; Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time); catching_up = 0; } Need_new_start_pos = 1; if (Player_ai->current_target_is_locked) { Players[Player_num].lock_indicator_x = Player->current_target_sx; Players[Player_num].lock_indicator_y = Player->current_target_sy; return; } delta_x = Players[Player_num].lock_indicator_x - Player->current_target_sx; delta_y = Players[Player_num].lock_indicator_y - Player->current_target_sy; if (!delta_y && !delta_x) { hypotenuse = 0.0f; } else { hypotenuse = _hypot(delta_y, delta_x); } Players[Player_num].lock_dist_to_target = (float)hypotenuse; if (last_dist_to_target == 0) { last_dist_to_target = Players[Player_num].lock_dist_to_target; } //nprintf(("Alan","dist to target: %.2f\n",Players[Player_num].lock_dist_to_target)); //nprintf(("Alan","last to target: %.2f\n\n",last_dist_to_target)); if (catching_up) { //nprintf(("Alan","IN CATCH UP MODE catch_up_dist is %.2f\n",catch_up_distance)); if ( Players[Player_num].lock_dist_to_target < catch_up_distance ) catching_up = 0; } else { //nprintf(("Alan","IN NORMAL MODE\n")); if ( (Players[Player_num].lock_dist_to_target - last_dist_to_target) > 2.0f ) { catching_up = 1; catch_up_distance = last_dist_to_target + wip->catchup_pixel_penalty; } } last_dist_to_target = Players[Player_num].lock_dist_to_target; if (!catching_up) { Players[Player_num].lock_time_to_target -= frametime; if (Players[Player_num].lock_time_to_target < 0.0f) Players[Player_num].lock_time_to_target = 0.0f; } float lock_pixels_per_sec; if (Players[Player_num].lock_time_to_target > 0) { lock_pixels_per_sec = Players[Player_num].lock_dist_to_target / Players[Player_num].lock_time_to_target; } else { lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec); } if (lock_pixels_per_sec > wip->lock_pixels_per_sec) { lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec); } if (catching_up) { pixels_moved_while_locking = wip->catchup_pixels_per_sec * frametime; } else { pixels_moved_while_locking = lock_pixels_per_sec * frametime; } if (delta_x != 0) { accumulated_x_pixels += pixels_moved_while_locking * delta_x/hypotenuse; } if (delta_y != 0) { accumulated_y_pixels += pixels_moved_while_locking * delta_y/hypotenuse; } if (fl_abs(accumulated_x_pixels) > 1.0f) { modf(accumulated_x_pixels, &int_portion); Players[Player_num].lock_indicator_x -= (int)int_portion; if ( fl_abs(Players[Player_num].lock_indicator_x - Player->current_target_sx) < fl_abs(int_portion) ) Players[Player_num].lock_indicator_x = Player->current_target_sx; accumulated_x_pixels -= int_portion; } if (fl_abs(accumulated_y_pixels) > 1.0f) { modf(accumulated_y_pixels, &int_portion); Players[Player_num].lock_indicator_y -= (int)int_portion; if ( fl_abs(Players[Player_num].lock_indicator_y - Player->current_target_sy) < fl_abs(int_portion) ) Players[Player_num].lock_indicator_y = Player->current_target_sy; accumulated_y_pixels -= int_portion; } if ( Missile_track_loop == -1 ) { Missile_track_loop = snd_play_looping( &Snds[SND_MISSILE_TRACKING], 0.0f , -1, -1); } if (!Players[Player_num].lock_time_to_target) { if ( (Players[Player_num].lock_indicator_x == Player->current_target_sx) && (Players[Player_num].lock_indicator_y == Player->current_target_sy) ) { if (maintain_lock_count++ > 1) { Player_ai->current_target_is_locked = 1; } } else { maintain_lock_count = 0; } } } else { if ( Missile_track_loop > -1 ) { snd_chg_loop_status(Missile_track_loop, 0); Missile_track_loop = -1; } Player_ai->current_target_is_locked = 0; if (!Players[Player_num].lock_indicator_visible) { return; } catching_up = 0; last_dist_to_target = 0.0f; if (Need_new_start_pos) { hud_calculate_lock_start_pos(); Need_new_start_pos = 0; accumulated_x_pixels = 0.0f; accumulated_y_pixels = 0.0f; } delta_x = Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x; delta_y = Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y; if (!delta_y && !delta_x) { hypotenuse = 0.0f; } else { hypotenuse = _hypot(delta_y, delta_x); } Players[Player_num].lock_time_to_target += frametime; if (Players[Player_num].lock_time_to_target > wip->min_lock_time) Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time); pixels_moved_while_degrading = 2.0f * wip->lock_pixels_per_sec * frametime; if (delta_x != 0) accumulated_x_pixels += pixels_moved_while_degrading * delta_x/hypotenuse; if (delta_y != 0) accumulated_y_pixels += pixels_moved_while_degrading * delta_y/hypotenuse; if (fl_abs(accumulated_x_pixels) > 1.0f) { modf(accumulated_x_pixels, &int_portion); Players[Player_num].lock_indicator_x -= (int)int_portion; if ( fl_abs(Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x) < fl_abs(int_portion) ) Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x; accumulated_x_pixels -= int_portion; } if (fl_abs(accumulated_y_pixels) > 1.0f) { modf(accumulated_y_pixels, &int_portion); Players[Player_num].lock_indicator_y -= (int)int_portion; if ( fl_abs(Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y) < fl_abs(int_portion) ) Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y; accumulated_y_pixels -= int_portion; } if ( (Players[Player_num].lock_indicator_x == Players[Player_num].lock_indicator_start_x) && (Players[Player_num].lock_indicator_y == Players[Player_num].lock_indicator_start_y) ) { Players[Player_num].lock_indicator_visible = 0; } } }
// hud_do_lock_indicator() manages missle locking, both the non-rendering calculations and the 2D HUD rendering void hud_do_lock_indicator(float frametime) { ship_weapon* swp; weapon_info* wip; vec3d lock_world_pos; // if i'm a multiplayer observer, bail here if ((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))) { return; } Assert(Player_ai->target_objnum >= 0); // be sure to unset this flag, then possibly set later in this function so that // threat indicators work properly. Player_ai->ai_flags &= ~AIF_SEEK_LOCK; if (hud_abort_lock()) { hud_lock_reset(); return; } // if there is an EMP effect active, never update lock if (emp_active_local()) { hud_lock_reset(); return; } swp = &Player_ship->weapons; wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; Lock_start_dist = wip->min_lock_time * wip->lock_pixels_per_sec; // if secondary weapons change, reset the lock if (hud_lock_secondary_weapon_changed(swp)) { hud_lock_reset(); } Player_ai->last_secondary_index = swp->current_secondary_bank; object* tobjp = &Objects[Player_ai->target_objnum]; vec3d dir_to_target; vm_vec_normalized_dir(&dir_to_target, &tobjp->pos, &Player_obj->pos); if (!(wip->wi_flags & WIF_LOCKED_HOMING)) { hud_lock_reset(); return; } if ((wip->wi_flags & WIF_HOMING_JAVELIN) && (Player->locking_subsys != NULL) && (Player->locking_subsys->current_hits <= 0.0f)) { hud_lock_reset(); } // Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags) if ((Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (Objects[Player_ai->target_objnum].type != OBJ_WEAPON)) { hud_lock_reset(); return; } // Javelins must lock on engines if locking on a ship and those must be in sight if (wip->wi_flags & WIF_HOMING_JAVELIN && tobjp->type == OBJ_SHIP && Player->locking_subsys != NULL) { vec3d subobj_pos; vm_vec_unrotate(&subobj_pos, &Player->locking_subsys->system_info->pnt, &tobjp->orient); vm_vec_add2(&subobj_pos, &tobjp->pos); int target_subsys_in_sight = ship_subsystem_in_sight(tobjp, Player->locking_subsys, &Player_obj->pos, &subobj_pos); if (!target_subsys_in_sight || Player->locking_subsys->system_info->type != SUBSYSTEM_ENGINE) { Player->locking_subsys = ship_get_closest_subsys_in_sight(&Ships[tobjp->instance], SUBSYSTEM_ENGINE, &Player_obj->pos); } } if (wip->wi_flags & WIF_HOMING_JAVELIN && tobjp->type == OBJ_SHIP && Player->locking_subsys == NULL) { Player->locking_subsys = ship_get_closest_subsys_in_sight(&Ships[tobjp->instance], SUBSYSTEM_ENGINE, &Player_obj->pos); if (Player->locking_subsys == NULL) { hud_lock_reset(); return; } } hud_lock_determine_lock_point(&lock_world_pos); if (!hud_lock_has_homing_point()) { Player->target_in_lock_cone = 0; } hud_lock_check_if_target_in_lock_cone(&lock_world_pos); // check if the target is within range of the current secondary weapon. If it is not, // a lock will not be detected if (!hud_lock_target_in_range()) { Player->target_in_lock_cone = 0; } // If locking on a subsystem, and not in sight... can't lock // Changed by MK on 4/3/98. It was confusing me that my hornets would not lock on my target. // It will now be confusing that they lock, but don't home on your subsystem, but I think that's preferable. // Often you really care about destroying the target, not just the subsystem. /*if ( Player_ai->targeted_subsys ) { if ( !hud_lock_on_subsys_ok() ) { Player->target_in_lock_cone=0; } }*/ if (!Player->target_in_lock_cone) { Player->locking_on_center = 0; Player->locking_subsys_parent = -1; Player->locking_subsys = NULL; } hud_calculate_lock_position(frametime); if (!Players[Player_num].lock_indicator_visible) return; if (Player_ai->current_target_is_locked) { if (Missile_track_loop > -1) { snd_chg_loop_status(Missile_track_loop, 0); Missile_track_loop = -1; Missile_lock_loop = snd_play(&Snds[SND_MISSILE_LOCK]); } } else { Player_ai->ai_flags |= AIF_SEEK_LOCK; // set this flag so multiplayer's properly track lock on other ships if (Missile_lock_loop != -1 && snd_is_playing(Missile_lock_loop)) { snd_stop(Missile_lock_loop); Missile_lock_loop = -1; } } hud_show_lock_indicator(frametime, &lock_world_pos); }