// create objects for all known observers in the game at level start // call this before entering a mission // this implies for the local player in the case of a client or for _all_ players in the case of a server void multi_obs_level_init() { int idx; // unset the OBS_PLAYER flag here for all net players for (idx = 0; idx < MAX_PLAYERS; idx++) { Net_players[idx].flags &= ~(NETINFO_FLAG_OBS_PLAYER); } // if i'm a client and I'm an observer, create an object for myself if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)) { // create my own observer object and setup other misc. data multi_obs_create_observer_client(); } // otherwise create stuff for all (permanent) observers in the game else { for (idx = 0; idx < MAX_PLAYERS; idx++) { if (MULTI_CONNECTED(Net_players[idx]) && MULTI_OBSERVER(Net_players[idx])) { // make an observer object for the guy multi_obs_create_observer(&Net_players[idx]); } } } }
// notify of a player leaving void multi_respawn_player_leave(net_player *pl) { // bogus if(pl == NULL){ return; } if( MULTI_OBSERVER((*pl)) ){ return; } if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ return; } if(pl->p_info.p_objp == NULL){ return; } // dogfight mode if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ return; } // if we need to respawn this ai ship, add him to a list of ships to get respawned p_object *pobjp = pl->p_info.p_objp; if ( (pobjp->flags & P_OF_PLAYER_START) && (pobjp->respawn_count < Netgame.respawn) ){ int i; for (i = 0; i < MAX_AI_RESPAWNS; i++ ) { if ( Ai_respawns[i].pobjp == NULL ) { Ai_respawns[i].pobjp = pobjp; Ai_respawns[i].timestamp = timestamp(AI_RESPAWN_TIME); break; } } } }
// called once per frame to update the reticle gauges. Makes calls to // ship_dumbfire_threat() and ship_lock_threat() and updates Threat_flags. void hud_update_reticle( player *pp ) { int rval; ship *shipp; // multiplayer clients won't call this routine if ( MULTIPLAYER_CLIENT || MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])) return; shipp = &Ships[Objects[pp->objnum].instance]; if ( ship_dumbfire_threat(shipp) ) { pp->threat_flags |= THREAT_DUMBFIRE; pp->update_dumbfire_time = timestamp(THREAT_UPDATE_DUMBFIRE_TIME); } if ( timestamp_elapsed(pp->update_dumbfire_time) ) { pp->update_dumbfire_time = timestamp(THREAT_UPDATE_DUMBFIRE_TIME); pp->threat_flags &= ~THREAT_DUMBFIRE; } if ( timestamp_elapsed(pp->update_lock_time) ) { pp->threat_flags &= ~(THREAT_LOCK | THREAT_ATTEMPT_LOCK); pp->update_lock_time = timestamp(THREAT_UPDATE_LOCK_TIME); rval = ship_lock_threat(shipp); if ( rval == 1 ) { pp->threat_flags |= THREAT_ATTEMPT_LOCK; } else if ( rval == 2 ) { pp->threat_flags |= THREAT_LOCK; } } if(Player->threat_flags & THREAT_LOCK ) { // a less hacked up version of the missile launch warning hud_start_text_flash(XSTR("Launch", 1507), THREAT_LOCK_FLASH, fl2i(THREAT_LOCK_FLASH/2.0f)); } if ( Player->threat_flags & (THREAT_ATTEMPT_LOCK) ) { if ( timestamp_elapsed(Threat_lock_timer) ) { Threat_lock_timer = timestamp(THREAT_LOCK_FLASH); Threat_lock_frame++; if ( Threat_lock_frame > 2 ) { Threat_lock_frame = 1; } if ( (Threat_lock_frame == 2) && (Player->threat_flags & THREAT_ATTEMPT_LOCK ) ) { snd_play( &Snds[ship_get_sound(Player_obj, SND_THREAT_FLASH)]); } } } }
// call once per level just before entering the mission void multi_df_level_pre_enter() { int idx; // if we're not in dogfight mode, do nothing if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){ return; } // go through all player ships and make them hostile for(idx=0; idx<MAX_PLAYERS; idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player != NULL) && (Net_players[idx].m_player->objnum >= 0) && (Objects[Net_players[idx].m_player->objnum].type == OBJ_SHIP)){ Ships[Objects[Net_players[idx].m_player->objnum].instance].team = Iff_traitor; } } // }
// get the total AWACS level for target to viewer // < 0.0f : untargetable // 0.0 - 1.0f : marginally targetable // >= 1.0f : fully targetable as normal float awacs_get_level(object *target, ship *viewer, int use_awacs) { Assert(target); // Goober5000 Assert(viewer); // Goober5000 vec3d dist_vec, subsys_pos; float closest = 0.0f; float test; int closest_index = -1; int idx, stealth_ship = 0, check_huge_ship = 0, friendly_stealth_invisible = 0; ship *shipp = NULL; ship_info *sip = NULL; int viewer_has_primitive_sensors = (viewer->flags[Ship::Ship_Flags::Primitive_sensors]); // calc distance from viewer to target vm_vec_sub(&dist_vec, &target->pos, &Objects[viewer->objnum].pos); int distance = (int) vm_vec_mag_quick(&dist_vec); // redone by Goober5000 #define ALWAYS_TARGETABLE 1.5f #define MARGINALLY_TARGETABLE 0.5f #define UNTARGETABLE -1.0f #define FULLY_TARGETABLE (viewer_has_primitive_sensors ? ((distance < viewer->primitive_sensor_range) ? MARGINALLY_TARGETABLE : UNTARGETABLE) : ALWAYS_TARGETABLE) // if the viewer is me, and I'm a multiplayer observer, its always viewable if ((viewer == Player_ship) && (Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])) return ALWAYS_TARGETABLE; // check the targeting threshold if ((Hud_max_targeting_range > 0) && (distance > Hud_max_targeting_range)) { return UNTARGETABLE; } if (target->type == OBJ_SHIP) { // if no valid target then bail as never viewable if (target->instance < 0) return UNTARGETABLE; // Goober5000 shipp = &Ships[target->instance]; sip = &Ship_info[shipp->ship_info_index]; stealth_ship = (shipp->flags[Ship::Ship_Flags::Stealth]); friendly_stealth_invisible = (shipp->flags[Ship::Ship_Flags::Friendly_stealth_invis]); check_huge_ship = (sip->is_huge_ship()); } int nebula_enabled = (The_mission.flags[Mission::Mission_Flags::Fullneb]); // ships on the same team are always viewable if ((target->type == OBJ_SHIP) && (shipp->team == viewer->team)) { // not necessarily now! -- Goober5000 if ( !(stealth_ship && friendly_stealth_invisible) ) return FULLY_TARGETABLE; } // check for a tagged ship. TAG'd ships are _always_ visible if (target->type == OBJ_SHIP) { Assert( shipp != NULL ); if (shipp->tag_left > 0.0f || shipp->level2_tag_left > 0.0f) return FULLY_TARGETABLE; } // only check for Awacs if stealth ship or Nebula mission // determine the closest awacs on our team if ((stealth_ship || nebula_enabled) && use_awacs) { for (idx=0; idx<Awacs_count; idx++) { // if not on the same team as the viewer if (Awacs[idx].team != viewer->team) continue; // if this awacs source has somehow become invalid if (Awacs[idx].objp->type != OBJ_SHIP) continue; // get the subsystem position if (!get_subsystem_pos(&subsys_pos, Awacs[idx].objp, Awacs[idx].subsys)) continue; // determine if its the closest // special case for HUGE_SHIPS if (check_huge_ship) { // check if inside bbox expanded by awacs_radius if (check_world_pt_in_expanded_ship_bbox(&subsys_pos, target, Awacs[idx].subsys->awacs_radius)) { closest_index = idx; break; } } // not a huge ship else { // get distance from Subsys to target vm_vec_sub(&dist_vec, &subsys_pos, &target->pos); test = vm_vec_mag_quick(&dist_vec); if (test > Awacs[idx].subsys->awacs_radius) continue; if ((closest_index == -1) || (test < closest)) { closest = test; closest_index = idx; break; } } } } // if this is a stealth ship if (stealth_ship) { // if the ship is within range of an awacs if (closest_index >= 0) { // if the nebula effect is active, stealth ships are only partially targetable if (nebula_enabled) return MARGINALLY_TARGETABLE; // otherwise it's targetable return FULLY_TARGETABLE; } // otherwise its completely hidden else { return UNTARGETABLE; } } // all other ships else { // if this is not a nebula mission, its always targetable if (!nebula_enabled) return FULLY_TARGETABLE; // if the ship is within range of an awacs, its fully targetable if (closest_index >= 0) return FULLY_TARGETABLE; // fully targetable at half the nebula value // modify distance by species float scan_nebula_range = Neb2_awacs * Species_info[Ship_info[viewer->ship_info_index].species].awacs_multiplier; // special case for huge ship - check inside expanded bounding boxes if (check_huge_ship) { if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, scan_nebula_range)) { if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, 0.5f * scan_nebula_range)) return FULLY_TARGETABLE; return MARGINALLY_TARGETABLE; } } // otherwise check straight up nebula numbers else { vm_vec_sub(&dist_vec, &target->pos, &Objects[viewer->objnum].pos); test = vm_vec_mag_quick(&dist_vec); if (test < (0.5f * scan_nebula_range)) return FULLY_TARGETABLE; else if (test < scan_nebula_range) return MARGINALLY_TARGETABLE; } // untargetable at longer range return UNTARGETABLE; } }
// create complete priority sorted escort list for all active ships // escorts - array of escort info // num_escorts - number of escorts requests in field of active ships // This will be culled to MAX_ESCORTS, selecting the top set from escorts void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts) { ship_obj *so; object *objp; // start with none on list *num_escorts = 0; int idx; // multiplayer dogfight if(MULTI_DOGFIGHT){ for(idx=0; idx<MAX_PLAYERS; idx++){ // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list\n")); break; } // is this a valid player if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ // add the ship escorts[*num_escorts].objnum = -1; escorts[*num_escorts].obj_signature = -1; escorts[*num_escorts].priority = -1; escorts[*num_escorts].np_id = Net_players[idx].player_id; escorts[*num_escorts].escort_hit_timer = 0; escorts[*num_escorts].escort_hit_next_flash = 0; escorts[*num_escorts].escort_show_bright = false; (*num_escorts)++; } } } // all others else { for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { Assert( so->objnum >= 0 && so->objnum < MAX_OBJECTS); if((so->objnum < 0) || (so->objnum >= MAX_OBJECTS)){ continue; } objp = &Objects[so->objnum]; Assert( objp->type == OBJ_SHIP ); if(objp->type != OBJ_SHIP){ continue; } // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list\n")); break; } // only process ships that might be on the list if ( !(Ships[objp->instance].flags[Ship::Ship_Flags::Escort]) ){ continue; } // only process ships that can be seen by sensors if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Hidden_from_sensors]) ){ continue; } // don't process most stealth ships if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Stealth]) ) { if ( Ships[objp->instance].team == Player_ship->team ) { // friendly stealths are only not seen when explicitly specified if ( Ships[objp->instance].flags[Ship::Ship_Flags::Friendly_stealth_invis] ) { continue; } } // non-friendly stealths are never seen else { continue; } } // don't process objects that should be dead if ( objp->flags[Object::Object_Flags::Should_be_dead] ) { continue; } // add the ship escorts[*num_escorts].objnum = so->objnum; escorts[*num_escorts].obj_signature = objp->signature; escorts[*num_escorts].priority = Ships[objp->instance].escort_priority; escorts[*num_escorts].np_id = -1; escorts[*num_escorts].escort_hit_timer = 0; escorts[*num_escorts].escort_hit_next_flash = 0; escorts[*num_escorts].escort_show_bright = false; (*num_escorts)++; } } }
// create complete priority sorted escort list for all active ships // escorts - array of escort info // num_escorts - number of escorts requests in field of active ships // This will be culled to MAX_ESCORTS, selecting the top set from escorts void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts) { ship_obj *so; object *objp; // start with none on list *num_escorts = 0; int idx; // multiplayer dogfight if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ for(idx=0; idx<MAX_PLAYERS; idx++){ // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list")); break; } // is this a valid player if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ // add the ship escorts[*num_escorts].objnum = -1; escorts[*num_escorts].obj_signature = -1; escorts[*num_escorts].priority = -1; escorts[*num_escorts].np_id = Net_players[idx].player_id; (*num_escorts)++; } } } // all others else { for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { Assert( so->objnum >= 0 && so->objnum < MAX_OBJECTS); if((so->objnum < 0) || (so->objnum >= MAX_OBJECTS)){ continue; } objp = &Objects[so->objnum]; Assert( objp->type == OBJ_SHIP ); if(objp->type != OBJ_SHIP){ continue; } // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list")); break; } // only process ships that might be on the list if ( !(Ships[objp->instance].flags & SF_ESCORT) ){ continue; } // only process ships that can be seen by sensors if ( (Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS) ){ continue; } // don't process most stealth ships if ( (Ships[objp->instance].flags2 & SF2_STEALTH) ) { if ( Ships[objp->instance].team == Player_ship->team ) { // friendly stealths are only not seen when explicitly specified if ( Ships[objp->instance].flags2 & SF2_FRIENDLY_STEALTH_INVIS ) { continue; } } // non-friendly stealths are never seen else { continue; } } // don't process ships that are dying, or objects that should be dead if ( (Ships[objp->instance].flags & (SF_DYING|SF_DEPARTING)) || (objp->flags & OF_SHOULD_BE_DEAD) ){ continue; } // add the ship escorts[*num_escorts].objnum = so->objnum; escorts[*num_escorts].obj_signature = objp->signature; escorts[*num_escorts].priority = Ships[objp->instance].escort_priority; escorts[*num_escorts].np_id = -1; (*num_escorts)++; } } }