//  maybe distribute the event/goal score between the players on that team
void multi_player_maybe_add_score (int score, int team)
{
	int players_in_team = 0; 
	int idx; 

	// if i'm not the server of a multiplayer game, bail here
	if(!MULTIPLAYER_MASTER){
		return;
	}

	if (!(The_mission.ai_profile->flags & AIPF_ALLOW_MULTI_EVENT_SCORING)) {
		return;
	}				

	// first count the number of players in this team
	for (idx=0; idx<MAX_PLAYERS; idx++) {
		if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == team)) {
			players_in_team++;
		}
	}

	if (!players_in_team) {
		return;
	}

	for (idx=0; idx<MAX_PLAYERS; idx++) {
		if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == team)) {
			Net_players[idx].m_player->stats.m_score += (int)(score * scoring_get_scale_factor() / players_in_team );
		}
	}
}
// (client) call when receiving a packet indicating we should pause
void multi_pause_pause()
{
	int idx;

	// if we're already paused, don't do anything
	if(Multi_pause_status){
		return;
	}

	// sanity check
	Assert(!Multi_pause_status);

	// mark the game as being paused
	Multi_pause_status = 1;

	// if we're not already in the pause state
	if(gameseq_get_state() != GS_STATE_MULTI_PAUSED){
		// jump into the paused state 
		gameseq_post_event(GS_EVENT_MULTI_PAUSE);

		// mark the netgame state
		Netgame.game_state = NETGAME_STATE_PAUSED;					
	}

	// if we're the server of the game, send a packet which will pause the clients in the game now
	if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
		for(idx=0;idx<MAX_PLAYERS;idx++){
			if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
				send_client_update_packet(&Net_players[idx]);
			}
		}
	}
}
// (client) call when receiving a packet indicating we should unpause
void multi_pause_unpause()
{
	int idx;

	// if we're already unpaused, don't do anything
	if(!Multi_pause_status){
		return;
	}

	// sanity check
	Assert(Multi_pause_status);

	// mark the game as being unpaused
	Multi_pause_status = 0;

	// pop us out of any necessary states (including the pause state !!)
	multi_handle_state_special();
	
	// mark the netgame state
	Netgame.game_state = NETGAME_STATE_IN_MISSION;			

	// if we're the server of the game, send a packet which will unpause the clients in the game now
	// if we're the server of the game, send a packet which will pause the clients in the game now
	if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
		for(idx=0;idx<MAX_PLAYERS;idx++){
			if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
				send_client_update_packet(&Net_players[idx]);
			}
		}
	}
}
// debug console function called to determine which player to kick
void multi_dcf_kick()
{
	int player_num,idx;
	SCP_string arg;

	// get the callsign of the player to kick
	dc_stuff_string(arg);

	player_num = -1;
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && (stricmp(Net_players[idx].m_player->callsign, arg.c_str()) == 0)) {
			player_num = idx;
			break;
		}
	}

	// if we didn't find the player, notify of the results
	if(player_num == -1){
		dc_printf("Could not find player %s to kick!", arg.c_str());
	} 
	// if we found the guy, then try and kick him
	else {
		multi_kick_player(player_num);
	}
}
// debug console function called to determine which player to kick
void multi_dcf_kick()
{
	int player_num,idx;

	// get the callsign of the player to kick
	dc_get_arg(ARG_STRING);

	if(strlen(Dc_arg) == 0){
		dc_printf("Invalid player callsign!\n");
		return ;
	}

	player_num = -1;
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && (stricmp(Net_players[idx].player->callsign,Dc_arg)==0)){
			player_num = idx;
			break;
		}
	}

	// if we didn't find the player, notify of the results
	if(player_num == -1){
		dc_printf("Could not find player %s to kick!",Dc_arg);
	} 
	// if we found the guy, then try and kick him
	else {
		multi_kick_player(player_num);
	}
}
// close
void multi_df_debrief_close()
{
	int idx;
	scoring_struct *sc;

	// shutdown the chatbox
	chatbox_close();

	// if stats weren't accepted, backout my own stats
	if (multi_debrief_stats_accept_code() != 1) {		
		// if stats weren't accepted, backout my own stats
		if (multi_debrief_stats_accept_code() != 1) {
			if(MULTIPLAYER_MASTER){
				for(idx=0; idx<MAX_PLAYERS; idx++){
					if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player != NULL)){
						sc = &Net_players[idx].m_player->stats;
						scoring_backout_accept(sc);

						if (Net_player == &Net_players[idx]) {
							Pilot.update_stats_backout( sc );
						}
					}
				}
			} else {
				scoring_backout_accept( &Player->stats );
				Pilot.update_stats_backout( &Player->stats );
			}
		}
	}

	// music stuff
	common_music_close();
}
示例#7
0
// 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]);
			}
		}
	}
}
// merge any mission stats accumulated into the alltime stats (as well as updating per campaign stats)
void scoring_level_close(int accepted)
{
	// want to calculate any other statistics.
	if (The_mission.game_type == MISSION_TYPE_TRAINING){
		// call scoring_do_accept
		// this will grant any potential medals and then early bail, and
		// then we will early bail
		scoring_do_accept(&Player->stats);
		Pilot.update_stats(&Player->stats, true);
		return;
	}

	if(accepted){
		// apply mission stats for all players in the game
		int idx;
		scoring_struct *sc;

		if(Game_mode & GM_MULTIPLAYER){
			nprintf(("Network","Storing stats for all players now\n"));
			for(idx=0;idx<MAX_PLAYERS;idx++){
				if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
					// get the scoring struct
					sc = &Net_players[idx].m_player->stats;
					scoring_do_accept( sc );

					if (Net_player == &Net_players[idx]) {
						Pilot.update_stats(sc);
					}
				}
			}
		} else {
			nprintf(("General","Storing stats now\n"));
			scoring_do_accept( &Player->stats );
		}

		// If this mission doesn't allow promotion or badges
		// then be sure that these don't get done.  Don't allow promotions or badges when
		// playing normally and not in a campaign.
		if ( (The_mission.flags & MISSION_FLAG_NO_PROMOTION) || ((Game_mode & GM_NORMAL) && !(Game_mode & GM_CAMPAIGN_MODE)) ) {
			if ( Player->stats.m_promotion_earned != -1) {
				Player->stats.rank--;
				Player->stats.m_promotion_earned = -1;
			}

			// if a badge was earned, take it back
			if ( Player->stats.m_badge_earned.size() ){
				for (size_t medal = 0; medal < Player->stats.m_badge_earned.size(); medal++) {
					Player->stats.medal_counts[Player->stats.m_badge_earned[medal]] = 0;
				}
				Player->stats.m_badge_earned.clear();
			}
		}

		if ( !(Game_mode & GM_MULTIPLAYER) ) {
			Pilot.update_stats(&Player->stats);
		}
	} 	
}
// evaluate if a wing SQUADMATE MESSAGE command should be sent to a player
// return 0 if at least one ai ship got the order, 1 if only players
int multi_msg_eval_wing_squadmsg(int wingnum,int command,ai_info *aif, int player_num)
{
	int idx;
	ushort net_sig;
	int subsys_type;
	int sent_count;

	// get the index of the sender
	if(player_num == -1)
		player_num = MY_NET_PLAYER_NUM;

	// get the target information
	if(aif->target_objnum == -1){
		net_sig = 0;
	} else {
		net_sig = Objects[aif->target_objnum].net_signature;
	}
	subsys_type = -1;
	if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){
		subsys_type = -1;
	} else {
		subsys_type = aif->targeted_subsys->system_info->type;
	}

	// go through all netplayers and find all matched
	sent_count = Wings[wingnum].current_count;
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){			
			// if he is in the wing, send him the message
			if(multi_msg_player_in_wing(wingnum,&Net_players[idx])){
				// if this was the sender himself, just decrement the count
				if(idx == player_num){
					sent_count--;
					continue;
				}

				// if its me, just display locally
				if(&Net_players[idx] == Net_player){
					multi_msg_show_squadmsg(&Net_players[player_num],command,net_sig,subsys_type);						
					sent_count--;
				}
				// otherwise send it to who is supposed to get it
				else {					
					multi_msg_send_squadmsg_packet(&Net_players[idx],&Net_players[player_num],command,net_sig,subsys_type);
					sent_count--;
				}
			}
		}
	}

	// if all the ships which got the message were players, return 1
	return !sent_count;
}
void hud_obs_render_players_all()
{
	int idx,count;

	// render kills and stats information for all players
	count = 0;
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) ){
			hud_obs_render_player(count,&Net_players[idx]);
		}
	}
}
示例#11
0
// send a ping to all players
void multi_ping_send_all()
{
	int idx;
	for(idx = 0;idx < MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != NULL) && (Net_player->player_id != Net_players[idx].player_id)){
			// start the ping
			multi_ping_start(&Net_players[idx].s_info.ping);			

			// send the ping packet
			send_ping(&Net_players[idx].p_info.addr);
		}
	}
}
示例#12
0
// do all sync related data stuff (server-side only)
void multi_data_do()
{
	int idx, p_idx;

	// only do this once a second
	if ((time(NULL) - Multi_data_time) < 1)
	{
		return;
	}

	// maybe try and reload player squad logo bitmaps
	multi_data_maybe_reload();

	// reset the time
	Multi_data_time = time(NULL);

	// if I'm the server
	if (Net_player && (Net_player->flags & NETINFO_FLAG_AM_MASTER))
	{
		// for all valid files
		for (idx = 0; idx < MAX_MULTI_DATA; idx++)
		{
			// a valid file that isn't currently xferring (ie, anything we've got completely on our hard drive)
			if (Multi_data[idx].used && (multi_xfer_lookup(Multi_data[idx].filename) < 0))
			{
				// send it to all players who need it
				for (p_idx = 0; p_idx < MAX_PLAYERS; p_idx++)
				{
					// if he doesn't have it
					if ((Net_player != &Net_players[p_idx]) &&
						MULTI_CONNECTED(Net_players[p_idx]) && (Net_players[p_idx].reliable_socket !=
																INVALID_SOCKET) &&
						(Multi_data[idx].status[p_idx] == 0))
					{
						// queue up the file to send to him, or at least try to
						if (multi_xfer_send_file(Net_players[p_idx].reliable_socket, Multi_data[idx].filename,
							CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE) < 0)
						{
							nprintf(("Network", "Failed to send data file! Trying again later...\n"));
						}
						else
						{
							// mark his status
							Multi_data[idx].status[p_idx] = 1;
						}
					}
				}
			}
		}
	}
}
示例#13
0
int find_netplayer_n(int n)
{
	int idx;
	int target;
   target = n;
	n=0;
   for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx])){
			n++;
			if(n == target)
				return idx;
		}
	}
	return -1;
}
示例#14
0
// process all kick details (disconnecting players who have been kicked but haven't closed their socket)
void multi_kick_process()
{
	int idx;

	// if i'm not the server, don't do anything
	if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
		return;
	}

	// disconnect any kicked players who have timed out on leaving
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.kick_timestamp != -1) && timestamp_elapsed(Net_players[idx].s_info.kick_timestamp) ){
			delete_player(idx, Net_players[idx].s_info.kick_reason);
		}
	}
}
示例#15
0
// server should check to see if any respawned players have run out of their invulnerability
void multi_respawn_handle_invul_players()
{
	int idx;
	object *objp;
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].flags & OF_INVULNERABLE)){	
			// make him normal (_non_ invulnerable) on either of 2 conditions :
			// 1.) More than 5 seconds have passed
			// 2.) He's fired either a primary or a secondary weapon
			if( ((Net_players[idx].s_info.invul_timestamp != -1) && (timestamp_elapsed(Net_players[idx].s_info.invul_timestamp))) ||
				 ((Net_players[idx].player->ci.fire_primary_count > 0) || (Net_players[idx].player->ci.fire_secondary_count > 0)) ) {
				objp = &Objects[Net_players[idx].player->objnum];
				obj_set_flags(objp,objp->flags & ~(OF_INVULNERABLE));
			}
		}
	}
}
示例#16
0
// maybe try and reload player squad logo bitmaps
void multi_data_maybe_reload()
{
	int idx;

	// go through all connected and try to reload their images if necessary
	for(idx=0; idx<MAX_PLAYERS; idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && strlen(Net_players[idx].m_player->m_squad_filename) && (Net_players[idx].m_player->insignia_texture == -1)){
			Net_players[idx].m_player->insignia_texture = bm_load_duplicate(Net_players[idx].m_player->m_squad_filename);

			// if the bitmap loaded properly, lock it as a transparent texture
			if(Net_players[idx].m_player->insignia_texture != -1){
				bm_lock(Net_players[idx].m_player->insignia_texture, 16, BMP_TEX_XPARENT);
				bm_unlock(Net_players[idx].m_player->insignia_texture);
			}
		}
	}	
}
// evaluate if a ship SQUADMATE MESSAGE command should be sent to a player
// return 0 if not sent to a netplayer, 1 if it was
int multi_msg_eval_ship_squadmsg(int shipnum,int command,ai_info *aif, int player_num)
{
	int idx;
	ushort net_sig;
	int subsys_type;

	// get the index of the sender
	if ( player_num == -1 )
		player_num = MY_NET_PLAYER_NUM;

	// get the target information
	if(aif->target_objnum == -1){
		net_sig = 0;
	} else {
		net_sig = Objects[aif->target_objnum].net_signature;
	}
	subsys_type = -1;
	if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){
		subsys_type = -1;
	} else {
		subsys_type = aif->targeted_subsys->system_info->type;
	}

	// go through all netplayers and find all matched
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (idx != player_num)){
			// if he is in the ship, send him the message
			if(multi_msg_player_in_ship(shipnum,&Net_players[idx])){
				// if its me, just display locall
				if(&Net_players[idx] == Net_player){				
					multi_msg_show_squadmsg(&Net_players[player_num],command,net_sig,subsys_type);						

					return 1;
				}
				// otherwise send it to who is supposed to get it
				else {
					multi_msg_send_squadmsg_packet(&Net_players[idx],&Net_players[player_num],command,net_sig,subsys_type);
					return 1;
				}													
			}
		}
	}

	// this will let the messaging system show a response to the sender of the packet
	return 0;
}
void FS2NetD_CheckDuplicateLogin()
{
	int buffer_size;
	char buffer[BASE_PACKET_SIZE + sizeof(int) + sizeof(ubyte) + (MAX_PLAYERS * sizeof(int)) + 10];
	int ids_count = 0;
	int *ids = new int[MAX_PLAYERS];
	int idx;

	if ( !ids ) {
		return;
	}

	for (idx = 0; idx < MAX_PLAYERS; idx++) {
		if ( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) ) {      
			if ( (Net_players[idx].tracker_player_id >= 0) && (Net_players[idx].tracker_player_id != Multi_tracker_id) ) {
				ids[ids_count] = Net_players[idx].tracker_player_id;
				ids_count++;
			}
		}
	}

	if ( !ids_count ) {
		delete [] ids;
		return;
	}

	INIT_PACKET( PCKT_DUP_LOGIN_RQST );

	PXO_ADD_INT( Multi_tracker_id );

	Assert( MAX_PLAYERS <= 255 );

	ubyte tvar = (ubyte)ids_count;
	PXO_ADD_DATA( tvar );

	for (idx = 0; idx < ids_count; idx++) {
		PXO_ADD_INT( ids[idx] );
	}

	DONE_PACKET();

	FS2NetD_SendData(buffer, buffer_size);

	delete [] ids;
}
// 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;
		}
	}

	// 
}
// lookup the "next" player in the netplayer list, return null if not found
net_player *multi_pinfo_get_next_player(net_player *np)
{
	int start_index;
	int idx;

	// get the starting index to look from
	start_index = NET_PLAYER_INDEX(np);	
	if(start_index < (MAX_PLAYERS - 1)){		
		// look forwards
		for(idx=start_index+1; idx<MAX_PLAYERS; idx++){
			if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
				return &Net_players[idx];
			}
		}
	}
	
	return NULL;
}
// lookup the "previous" player in the netplayer list, return null if not found
net_player *multi_pinfo_get_prev_player(net_player *np)
{
	int start_index;
	int idx;

	// get the starting index to look from
	start_index = NET_PLAYER_INDEX(np);
	if(start_index > 0){		
		// look backwards
		for(idx=start_index-1; idx>=0; idx--){
			if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
				return &Net_players[idx];
			}
		}
	}
	
	return NULL;
}
// called when server is waiting for clients to disconnect
int multi_endgame_server_ok_to_leave()
{
	int idx;
	
	// check to see if our client disconnect timestamp has elapsed
	if ( Multi_endgame_server_wait_stamp > 0.0f ) {
		Multi_endgame_server_wait_stamp -= flFrametime;
		if ( Multi_endgame_server_wait_stamp <= 0.0f ) {
			return 1;
		}
	}
		
	// check to see if all clients have disconnected
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
			return 0;
		}
	}

	// all conditions passed
	return 1;
}
// setup kill matrix data
void multi_df_setup_kill_matrix()
{
	int idx, s_idx;
	multi_df_score *s;

	Multi_df_score_count = 0;

	// add players as necessary
	for(idx=0; idx<MAX_PLAYERS; idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player != NULL)){
			// stuff data for this guy
			s = &Multi_df_score[Multi_df_score_count++];

			ml_printf("Dogfight debrief stats for %s", Net_players[idx].m_player->callsign);
			for(s_idx=0; s_idx<MAX_PLAYERS; s_idx++){
				ml_printf("%d", Net_players[idx].m_player->stats.m_dogfight_kills[s_idx]);
			}

			s->stats = Net_players[idx].m_player->stats;
			strcpy_s(s->callsign, Net_players[idx].m_player->callsign);			
			s->np_index = idx;
		}
	}
}
示例#24
0
// 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)++;			
		}
	}
}
// evaluate a kill on a ship
int scoring_eval_kill(object *ship_objp)
{		
	float max_damage_pct;		// the pct% of total damage the max damage object did
	int max_damage_index;		// the index into the dying ship's damage_ship[] array corresponding the greatest amount of damage
	int killer_sig;				// signature of the guy getting credit for the kill (or -1 if none)
	int idx,net_player_num;
	player *plr;					// pointer to a player struct if it was a player who got the kill
	net_player *net_plr = NULL;
	ship *dead_ship;				// the ship which was killed
	net_player *dead_plr = NULL;
	float scoring_scale_by_damage = 1;	// percentage to scale the killer's score by if we score based on the amount of damage caused
	int kill_score, assist_score; 
	bool is_enemy_player = false;		// true if the player just killed an enemy player ship


	// multiplayer clients bail here
	if(MULTIPLAYER_CLIENT){
		return -1;
	}

	// we don't evaluate kills on anything except ships
	if(ship_objp->type != OBJ_SHIP){
		return -1;	
	}
	if((ship_objp->instance < 0) || (ship_objp->instance >= MAX_SHIPS)){
		return -1;
	}

	// assign the dead ship
	dead_ship = &Ships[ship_objp->instance];

	// evaluate player deaths
	if(Game_mode & GM_MULTIPLAYER){
		net_player_num = multi_find_player_by_object(ship_objp);
		if(net_player_num != -1){
			Net_players[net_player_num].m_player->stats.m_player_deaths++;
			nprintf(("Network","Setting player %s deaths to %d\n",Net_players[net_player_num].m_player->callsign,Net_players[net_player_num].m_player->stats.m_player_deaths));
			dead_plr = &Net_players[net_player_num];
			is_enemy_player = true;
		}
	} else {
		if(ship_objp == Player_obj){
			Player->stats.m_player_deaths++;
		}
	}

	net_player_num = -1;

	// clear out invalid damager ships
	for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){
		if((dead_ship->damage_ship_id[idx] >= 0) && (ship_get_by_signature(dead_ship->damage_ship_id[idx]) < 0)){
			dead_ship->damage_ship[idx] = 0.0f;
			dead_ship->damage_ship_id[idx] = -1;
		}
	}
			
	// determine which object did the most damage to the dying object, and how much damage that was
	max_damage_index = -1;
	for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
		// bogus ship
		if(dead_ship->damage_ship_id[idx] < 0){
			continue;
		}

		// if this slot did more damage then the next highest slot
		if((max_damage_index == -1) || (dead_ship->damage_ship[idx] > dead_ship->damage_ship[max_damage_index])){
			max_damage_index = idx;
		}			
	}
	
	// doh
	if((max_damage_index < 0) || (max_damage_index >= MAX_DAMAGE_SLOTS)){
		return -1;
	}

	// the pct of total damage applied to this ship
	max_damage_pct = dead_ship->damage_ship[max_damage_index] / dead_ship->total_damage_received;
    
    CLAMP(max_damage_pct, 0.0f, 1.0f);

	// only evaluate if the max damage % is high enough to record a kill and it was done by a valid object
	if((max_damage_pct >= Kill_percentage) && (dead_ship->damage_ship_id[max_damage_index] >= 0)){
		// set killer_sig for this ship to the signature of the guy who gets credit for the kill
		killer_sig = dead_ship->damage_ship_id[max_damage_index];

		// set the scale value if we only award 100% score for 100% damage
		if (The_mission.ai_profile->flags & AIPF_KILL_SCORING_SCALES_WITH_DAMAGE) {
			scoring_scale_by_damage = max_damage_pct;
		}

		// null this out for now
		plr = NULL;
		net_plr = NULL;

		// get the player (whether single or multiplayer)
		net_player_num = -1;

		if(Game_mode & GM_MULTIPLAYER){
			net_player_num = multi_find_player_by_signature(killer_sig);
			if(net_player_num != -1){
				plr = Net_players[net_player_num].m_player;
				net_plr = &Net_players[net_player_num];
			}
		} else {
			if(Objects[Player->objnum].signature == killer_sig){
				plr = Player;
			}
		}		

		// if we found a valid player, evaluate some kill details
		if(plr != NULL){
			int si_index;

			// bogus
			if((plr->objnum < 0) || (plr->objnum >= MAX_OBJECTS)){
				return -1;
			}			

			// get the ship info index of the ship type of this kill.  we need to take ship
			// copies into account here.
			si_index = dead_ship->ship_info_index;
			if (Ship_info[si_index].flags & SIF_SHIP_COPY)
			{
				char temp[NAME_LENGTH];
				strcpy_s(temp, Ship_info[si_index].name);
				end_string_at_first_hash_symbol(temp);

				// Goober5000 - previous error checking guarantees that this will be >= 0
				si_index = ship_info_lookup(temp);	
			}

			// if he killed a guy on his own team increment his bonehead kills
			if((Ships[Objects[plr->objnum].instance].team == dead_ship->team) && !MULTI_DOGFIGHT ){
				if (!(The_mission.flags & MISSION_FLAG_NO_TRAITOR)) {
					plr->stats.m_bonehead_kills++;
					kill_score = -(int)(dead_ship->score * scoring_get_scale_factor());
					plr->stats.m_score += kill_score;

					if(net_plr != NULL ) {
						multi_team_maybe_add_score(-(dead_ship->score), net_plr->p_info.team);
					}
				}
			} 
			// otherwise increment his valid kill count and score
			else {
				// dogfight mode
				if(MULTI_DOGFIGHT && (multi_find_player_by_object(ship_objp) < 0)){
					// don't add a kill for dogfight kills on non-players
				} else {
					plr->stats.m_okKills[si_index]++;		
					plr->stats.m_kill_count_ok++;

					// only computer controlled enemies should scale with difficulty
					if (is_enemy_player) {
						kill_score = (int)(dead_ship->score * scoring_scale_by_damage);
					}
					else {
						kill_score = (int)(dead_ship->score * scoring_get_scale_factor() * scoring_scale_by_damage);
					}


					plr->stats.m_score += kill_score;  					
					hud_gauge_popup_start(HUD_KILLS_GAUGE);

#ifdef SCORING_DEBUG
					char kill_score_text[1024] = "";
					sprintf(kill_score_text, "SCORING : %s killed a ship worth %d points and gets %d pts for the kill\n", plr->callsign, dead_ship->score, kill_score);	
					if (MULTIPLAYER_MASTER) {
						send_game_chat_packet(Net_player, kill_score_text, MULTI_MSG_ALL);
					}
					HUD_printf(kill_score_text);
					mprintf((kill_score_text));
#endif

					// multiplayer
					if(net_plr != NULL){
						multi_team_maybe_add_score(dead_ship->score , net_plr->p_info.team);

						// award teammates % of score value for big ship kills
						// not in dogfight tho
						// and not if there is no assist threshold (as otherwise assists could get higher scores than kills)
						if (!(Netgame.type_flags & NG_TYPE_DOGFIGHT) && (Ship_info[dead_ship->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
							for (idx=0; idx<MAX_PLAYERS; idx++) {
								if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == net_plr->p_info.team) && (&Net_players[idx] != net_plr)) {
									assist_score = (int)(dead_ship->score * The_mission.ai_profile->assist_award_percentage_scale[Game_skill_level]);
									Net_players[idx].m_player->stats.m_score += assist_score;

#ifdef SCORING_DEBUG
									// DEBUG CODE TO TEST NEW SCORING
									char score_text[1024] = "";
									sprintf(score_text, "SCORING : All team mates get %d pts for helping kill the capship\n", assist_score);
									send_game_chat_packet(Net_player, score_text, MULTI_MSG_ALL);
									HUD_printf(score_text);
									mprintf((score_text));
#endif
								}
							}
						}

						// death message
						if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (net_plr != NULL) && (dead_plr != NULL) && (net_plr->m_player != NULL) && (dead_plr->m_player != NULL)){
							char dead_text[1024] = "";

							sprintf(dead_text, "%s gets the kill for %s", net_plr->m_player->callsign, dead_plr->m_player->callsign);							
							send_game_chat_packet(Net_player, dead_text, MULTI_MSG_ALL, NULL, NULL, 2);
							HUD_printf(dead_text);
						}
					}
				}
			}
				
			// increment his all-encompassing kills
			plr->stats.m_kills[si_index]++;
			plr->stats.m_kill_count++;			
			
			// update everyone on this guy's kills if this is multiplayer
			if(MULTIPLAYER_MASTER && (net_player_num != -1)){
				// send appropriate stats
				if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
					// evaluate dogfight kills
					multi_df_eval_kill(&Net_players[net_player_num], ship_objp);

					// update stats
					send_player_stats_block_packet(&Net_players[net_player_num], STATS_DOGFIGHT_KILLS);
				} else {
					send_player_stats_block_packet(&Net_players[net_player_num], STATS_MISSION_KILLS);
				}				
			}
		}
	} else {
		// set killer_sig for this ship to -1, indicating no one got the kill for it
		killer_sig = -1;
	}		
		
	// pass in the guy who got the credit for the kill (if any), so that he doesn't also
	// get credit for an assist
	scoring_eval_assists(dead_ship,killer_sig, is_enemy_player);	

#ifdef SCORING_DEBUG

	if (Game_mode & GM_MULTIPLAYER) {
		char buf[256];
		sprintf(Scoring_debug_text, "SCORING : %s killed.\nDamage by ship:\n\n", Ship_info[dead_ship->ship_info_index].name);

		// show damage done by player
		for (int i=0; i<MAX_DAMAGE_SLOTS; i++) {
			int net_player_num = multi_find_player_by_signature(dead_ship->damage_ship_id[i]);
			if (net_player_num != -1) {
				plr = Net_players[net_player_num].m_player;
				sprintf(buf, "%s: %f", plr->callsign, dead_ship->damage_ship[i]);

				if (dead_ship->damage_ship_id[i] == killer_sig ) {
					strcat_s(buf, "  KILLER\n");
				} else {
					strcat_s(buf, "\n");
				}

				strcat_s(Scoring_debug_text, buf);	
			}
		}
		mprintf ((Scoring_debug_text)); 
	}
#endif

	return max_damage_index; 
}
示例#26
0
// 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)++;			
		}
	}
}
// blit the kill matrix
void multi_df_blit_kill_matrix()
{
	int idx, s_idx, str_len;
	int cx, cy;
	char squashed_string[CALLSIGN_LEN+1] = "";
	int dy = gr_get_font_height() + 1;

	// max width of an individual item, and the text that can be in that item
	float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1);
	float max_text_width = max_item_width * 0.8f;

	// start x for the top bar (one item to the right)
	int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width;
	int top_y_start = Multi_df_display_coords[gr_screen.res][1];	

	// start x for the side bar
	int side_x_start = Multi_df_display_coords[gr_screen.res][0];
	int side_y_start = Multi_df_display_coords[gr_screen.res][1] + dy;

	// draw the top bar
	cx = top_x_start;
	cy = top_y_start;
	for(idx=0; idx<Multi_df_score_count; idx++){		
		// force the string to fit nicely
		strcpy_s(squashed_string, Multi_df_score[idx].callsign);
		font::force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
		gr_get_string_size(&str_len, NULL, squashed_string);

		// set color and blit the string		
		Assert(Multi_df_score[idx].np_index >= 0);
		if(Multi_df_score[idx].np_index >= 0){
			gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
		}
		gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string, GR_RESIZE_MENU);

		// next spot
		cx += (int)max_item_width;
	}

	// draw the rest of the scoreboard	
	cx = side_x_start;
	cy = side_y_start;
	int row_total;
	for(idx=0; idx<Multi_df_score_count; idx++){		
		// draw a check if necessary
		if(!MULTI_CONNECTED(Net_players[Multi_df_score[idx].np_index]) || (Net_players[Multi_df_score[idx].np_index].state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (Net_players[Multi_df_score[idx].np_index].state == NETPLAYER_STATE_DEBRIEF_REPLAY)){
			if(Multi_common_icons[MICON_VALID] != -1){
				gr_set_bitmap(Multi_common_icons[MICON_VALID]);
				gr_bitmap(Multi_df_check_coords[gr_screen.res], cy, GR_RESIZE_MENU);
			}
		}

		// draw the name
		cx = Multi_df_display_coords[gr_screen.res][0];
		strcpy_s(squashed_string, Multi_df_score[idx].callsign);
		font::force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
		gr_get_string_size(&str_len, NULL, squashed_string);		
		Assert(Multi_df_score[idx].np_index >= 0);
		if(Multi_df_score[idx].np_index >= 0){
			gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
		}
		gr_string(cx, cy, squashed_string, GR_RESIZE_MENU);

		cx = top_x_start;
		row_total = 0;
		for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){
			// stuff the string to be displayed and select the proper display color
			if(s_idx == idx){
				strcpy_s(squashed_string, "-");
				gr_set_color_fast(&Color_grey);
			} else {
				row_total += multi_df_stuff_kills(squashed_string, idx, s_idx);
				Assert(Multi_df_score[idx].np_index >= 0);
				if(Multi_df_score[idx].np_index >= 0){
					gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
				}				
			}						

			// draw the string
			font::force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width);
			gr_get_string_size(&str_len, NULL, squashed_string);
			gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string, GR_RESIZE_MENU);

			// next spot
			cx += (int)max_item_width;
		}

		// draw the row total
		gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]);
		sprintf(squashed_string, "(%d)", row_total);
		gr_get_string_size(&str_len, NULL, squashed_string);
		gr_string(Multi_df_display_coords[gr_screen.res][0] + Multi_df_display_coords[gr_screen.res][2] - (MULTI_DF_TOTAL_ADJUST + str_len), cy, squashed_string, GR_RESIZE_MENU);

		cy += dy;
	}
}
// process an incoming multi options packet
void multi_options_process_packet(unsigned char *data, header *hinfo)
{
	ubyte code;	
	multi_local_options bogus;
	int idx,player_index;
	char str[255];
	int offset = HEADER_LENGTH;

	// find out who is sending this data	
	player_index = find_player_id(hinfo->id);

	if (player_index < 0) {
		nprintf(("Network", "Received packet from unknown player!\n"));
		return;
	}

	// get the packet code
	GET_DATA(code);
	switch(code){
	// get the start game options
	case MULTI_OPTION_START_GAME:
		Assert(Game_mode & GM_STANDALONE_SERVER);

		// get the netgame name
		GET_STRING(Netgame.name);		

		// get the netgame mode
		GET_INT(Netgame.mode);

		// get the security #
		GET_INT(Netgame.security);

		// get mode specific data
		switch(Netgame.mode){
		case NG_MODE_PASSWORD:
			GET_STRING(Netgame.passwd);
			break;
		case NG_MODE_RANK_ABOVE:
		case NG_MODE_RANK_BELOW:
			GET_INT(Netgame.rank_base);
			break;
		}

		// update standalone stuff
		std_connect_set_gamename(Netgame.name);
		std_multi_update_netgame_info_controls();
		break;

	// get mission choice options
	case MULTI_OPTION_MISSION:
		netgame_info ng;
		char title[NAME_LENGTH+1];
		int campaign_type,max_players;
		
		memset(&ng,0,sizeof(netgame_info));

		Assert(Game_mode & GM_STANDALONE_SERVER);

		// coop or team vs. team mode
		GET_INT(ng.type_flags);
		if((ng.type_flags & NG_TYPE_TEAM) && !(Netgame.type_flags & NG_TYPE_TEAM)){
			multi_team_reset();
		}
		// if squad war was switched on
		if((ng.type_flags & NG_TYPE_SW) && !(Netgame.type_flags & NG_TYPE_SW)){
			mprintf(("STANDALONE TURNED ON SQUAD WAR!!\n"));
		}
		Netgame.type_flags = ng.type_flags;

		// new respawn count
		GET_UINT(Netgame.respawn);

		// name string
		memset(str,0,255);

		GET_DATA(code);
		// campaign mode
		if(code){
			GET_STRING(ng.campaign_name);

			// set the netgame max players here if the filename has changed
			if(strcmp(Netgame.campaign_name,ng.campaign_name) != 0){				
				memset(title,0,NAME_LENGTH+1);			
				if(!mission_campaign_get_info(ng.campaign_name,title,&campaign_type,&max_players)){
					Netgame.max_players = 0;
				} else {
					Netgame.max_players = max_players;
				}

				strcpy_s(Netgame.campaign_name,ng.campaign_name);
			}

			Netgame.campaign_mode = 1;

			// put brackets around the campaign name
			if(Game_mode & GM_STANDALONE_SERVER){
				strcpy_s(str,"(");
				strcat_s(str,Netgame.campaign_name);
				strcat_s(str,")");
				std_multi_set_standalone_mission_name(str);
			}
		}
		// non-campaign mode
		else {
			GET_STRING(ng.mission_name);

			if(strcmp(Netgame.mission_name,ng.mission_name) != 0){
				if(strlen(ng.mission_name)){
					Netgame.max_players = mission_parse_get_multi_mission_info( ng.mission_name );
				} else {
					// setting this to -1 will prevent us from being seen on the network
					Netgame.max_players = -1;				
				}
				strcpy_s(Netgame.mission_name,ng.mission_name);
				strcpy_s(Game_current_mission_filename,Netgame.mission_name);				
			}			

			Netgame.campaign_mode = 0;
            
			// set the mission name
			if(Game_mode & GM_STANDALONE_SERVER){
				std_multi_set_standalone_mission_name(Netgame.mission_name);			
			}
		}

		// update FS2NetD as well
		if (MULTI_IS_TRACKER_GAME) {
			fs2netd_gameserver_update(true);
		}

		send_netgame_update_packet();	   
		break;

	// get the netgame options
	case MULTI_OPTION_SERVER:		
		get_server_options(data, &offset, &Netgame.options);

		// if we're a standalone set for no sound, do so here
		if((Game_mode & GM_STANDALONE_SERVER) && !Multi_options_g.std_voice){
			Netgame.options.flags |= MSO_FLAG_NO_VOICE;
		} else {
			// maybe update the quality of sound
			multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
		}

		// set the skill level
		Game_skill_level = Netgame.options.skill_level;		

		if((Game_mode & GM_STANDALONE_SERVER) && !(Game_mode & GM_CAMPAIGN_MODE)){
			Netgame.respawn = Netgame.options.respawn;
		}

		// if we have the "temp closed" flag toggle
		if(Netgame.options.flags & MLO_FLAG_TEMP_CLOSED){
			Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
		}
		Netgame.options.flags &= ~(MLO_FLAG_TEMP_CLOSED);

		// if i'm the standalone server, I should rebroadcast to all other players
		if(Game_mode & GM_STANDALONE_SERVER){
			for(idx=0;idx<MAX_PLAYERS;idx++){
				if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && (&Net_players[idx] != &Net_players[player_index]) ){
					multi_io_send_reliable(&Net_players[idx], data, offset);
				}
			}

			send_netgame_update_packet();
		}
		break;
	
	// local netplayer options
	case MULTI_OPTION_LOCAL:
		if(player_index == -1){
			get_local_options(data, &offset, &bogus);
		} else {
			get_local_options(data, &offset, &Net_players[player_index].p_info.options);

			//If the client has sent an object update higher than that which the server allows, reset it
			if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
				if (Net_players[player_index].p_info.options.obj_update_level > Cmdline_objupd) {
					Net_players[player_index].p_info.options.obj_update_level = Cmdline_objupd;
				}
			}
		}		
		break;
	}
	PACKET_SET_SIZE();
}