/*
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
*/

#ifdef NETWORK

#define DOS4G

#pragma off (unreferenced)
static char rcsid[] = "$Id: multi.c 2.208 1996/10/29 18:37:02 jason Exp $";
#pragma on (unreferenced)

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#include "game.h"
#include "modem.h"
#include "network.h"
#include "multi.h"
#include "object.h"
#include "laser.h"
#include "fuelcen.h"
#include "scores.h"
#include "gauges.h"
#include "collide.h"
#include "error.h"
#include "fireball.h"
#include "newmenu.h"
#include "mono.h"
#include "wall.h"
#include "cntrlcen.h"
#include "powerup.h"
#include "polyobj.h"
#include "bm.h"
#include "endlevel.h"
#include "key.h"
#include "playsave.h"
#include "timer.h"
#include "digi.h"
#include "sounds.h"
#include "kconfig.h"
#include "newdemo.h"
#include "text.h"
#include "kmatrix.h"
#include "glfmodem.h"
#include "commlib.h"
#include "multibot.h"
#include "gameseq.h"
#include "physics.h"
#include "config.h"
#include "state.h"
#include "ai.h"
#include "switch.h"
#include "textures.h"
#include "byteswap.h"
#include "sounds.h"
#include "args.h"

//
// Local macros and prototypes
//

// LOCALIZE ME!!

#define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0

void reset_player_object(void); // In object.c but not in object.h
void drop_player_eggs(object *player); // from collide.c
void GameLoop(int, int); // From game.c

//
// Global variables
//

extern vms_vector MarkerPoint[];
extern char MarkerMessage[16][40];
extern char MarkerOwner[16][40];
extern int MarkerObject[];

int control_invul_time = 0;
int who_killed_controlcen = -1;  // -1 = noone

//do we draw the kill list on the HUD?
int Show_kill_list = 1;
int Show_reticle_name = 1;
fix Show_kill_list_timer = 0;

char Multi_is_guided=0;
char PKilledFlags[MAX_NUM_NET_PLAYERS];

int multi_sending_message = 0;
int multi_defining_message = 0;
int multi_message_index = 0;

char multibuf[MAX_MULTI_MESSAGE_LEN+4];                // This is where multiplayer message are built

short remote_to_local[MAX_NUM_NET_PLAYERS][MAX_OBJECTS];  // Remote object number for each local object
short local_to_remote[MAX_OBJECTS]; 
byte  object_owner[MAX_OBJECTS];   // Who created each object in my universe, -1 = loaded at start

int     Net_create_objnums[MAX_NET_CREATE_OBJECTS]; // For tracking object creation that will be sent to remote
int     Net_create_loc = 0;  // pointer into previous array
int     Network_laser_fired = 0; // How many times we shot
int     Network_laser_gun; // Which gun number we shot
int   Network_laser_flags; // Special flags for the shot
int   Network_laser_level; // What level
short   Network_laser_track; // Who is it tracking?
char    Network_message[MAX_MESSAGE_LEN];
char  Network_message_macro[4][MAX_MESSAGE_LEN];
int     Network_message_reciever=-1;
int     sorted_kills[MAX_NUM_NET_PLAYERS];
short kill_matrix[MAX_NUM_NET_PLAYERS][MAX_NUM_NET_PLAYERS];
int     multi_goto_secret = 0;
short   team_kills[2];
int     multi_in_menu = 0;
int     multi_leave_menu = 0;
int     multi_quit_game = 0;

netgame_info Netgame;
AllNetPlayers_info NetPlayers;

bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES];

typedef struct netplayer_stats {
	ubyte           message_type;
	ubyte           Player_num;                                             // Who am i?
	uint            flags;                                                  // Powerup flags, see below...
	fix             energy;                                                 // Amount of energy remaining.
	fix             shields;                                                        // shields remaining (protection) 
	ubyte           lives;                                                  // Lives remaining, 0 = game over.
	ubyte           laser_level;                                    //      Current level of the laser.
	ubyte           primary_weapon_flags;                                   //      bit set indicates the player has this weapon.
	ubyte           secondary_weapon_flags;                                 //      bit set indicates the player has this weapon.
	ushort  primary_ammo[MAX_PRIMARY_WEAPONS];      // How much ammo of each type.
	ushort  secondary_ammo[MAX_SECONDARY_WEAPONS]; // How much ammo of each type.
	int             last_score;                                     // Score at beginning of current level.
	int             score;                                                  // Current score.
	fix             cloak_time;                                             // Time cloaked
	fix             invulnerable_time;                      // Time invulnerable
	fix             homing_object_dist;                     //      Distance of nearest homing object.
	short           KillGoalCount;
	short           net_killed_total;                               // Number of times killed total
	short           net_kills_total;                                // Number of net kills total
	short           num_kills_level;                                // Number of kills this level
	short           num_kills_total;                                // Number of kills total
	short           num_robots_level;                       // Number of initial robots this level
	short           num_robots_total;                       // Number of robots total
	ushort  hostages_rescued_total;         // Total number of hostages rescued.
	ushort  hostages_total;                         // Total number of hostages.
	ubyte           hostages_on_board;                      //      Number of hostages on ship.
	ubyte           unused[16];
} netplayer_stats;                                                      

int message_length[MULTI_MAX_TYPE+1] = {
	24, // POSITION
	3,  // REAPPEAR
	8,  // FIRE
	5,  // KILL
	4,  // REMOVE_OBJECT
	97+9, // PLAYER_EXPLODE
	37, // MESSAGE (MAX_MESSAGE_LENGTH = 40)
	2,  // QUIT
	4,  // PLAY_SOUND
	41, // BEGIN_SYNC
	4,  // CONTROLCEN
	5,  // CLAIM ROBOT
	4,  // END_SYNC
   2,  // CLOAK
	3,  // ENDLEVEL_START
   5,  // DOOR_OPEN
	2,  // CREATE_EXPLOSION
	16, // CONTROLCEN_FIRE
	97+9, // PLAYER_DROP
	19, // CREATE_POWERUP
	9,  // MISSILE_TRACK
	2,  // DE-CLOAK
	2,       // MENU_CHOICE
	28, // ROBOT_POSITION  (shortpos_length (23) + 5 = 28)
	9,  // ROBOT_EXPLODE
	5,       // ROBOT_RELEASE
	18, // ROBOT_FIRE
	6,  // SCORE
	6,  // CREATE_ROBOT
	3,  // TRIGGER
	10, // BOSS_ACTIONS     
	27, // ROBOT_POWERUPS
	7,  // HOSTAGE_DOOR
	2+24,   //SAVE_GAME             (ubyte slot, uint id, char name[20])
	2+4,   //RESTORE_GAME   (ubyte slot, uint id)
	1+1,    // MULTI_REQ_PLAYER
	sizeof(netplayer_stats),                        // MULTI_SEND_PLAYER
	55, // MULTI_MARKER
	12, // MULTI_DROP_WEAPON
#ifndef MACINTOSH
	3+sizeof(shortpos), // MULTI_GUIDED, IF SHORTPOS CHANGES, CHANGE MAC VALUE BELOW
#else
	26,	//MULTI_GUIDED IF SIZE OF SHORTPOS CHANGES, CHANGE THIS VALUE AS WELL!!!!!!
#endif
	11, // MULTI_STOLEN_ITEMS
	6, // MULTI_WALL_STATUS
	5, // MULTI_HEARTBEAT
	9, // MULTI_KILLGOALS
   9, // MULTI_SEISMIC
	18, // MULTI_LIGHT
   2, // MULTI_START_TRIGGER     
	6, // MULTI_FLAGS
	2, // MULTI_DROP_BLOB
	MAX_POWERUP_TYPES+1, // MULTI_POWERUP_UPDATE
	sizeof(active_door)+3, // MULTI_ACTIVE_DOOR
	4, // MULTI_SOUND_FUNCTION
	2, // MULTI_CAPTURE_BONUS
   2, // MULTI_GOT_FLAG
	12, // MULTI_DROP_FLAG
   142, // MULTI_ROBOT_CONTROLS
   2, // MULTI_FINISH_GAME
   3, // MULTI_RANK
   1, // MULTI_MODEM_PING
   1, // MULTI_MODEM_PING_RETURN
	3, // MULTI_ORB_BONUS
   2, // MULTI_GOT_ORB
	12, // MULTI_DROP_ORB
	4,  // MULTI_PLAY_BY_PLAY
};

char PowerupsInMine[MAX_POWERUP_TYPES],MaxPowerupsAllowed[MAX_POWERUP_TYPES];
extern fix ThisLevelTime;

//
//  Functions that replace what used to be macros
//              

int objnum_remote_to_local(int remote_objnum, int owner)
{
	// Map a remote object number from owner to a local object number

	int result;     

	if ((owner >= N_players) || (owner < -1)) {
		Int3(); // Illegal!
		return(remote_objnum);
	}

	if (owner == -1)
		return(remote_objnum);
	
	if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS))
		return(-1);

	result = remote_to_local[owner][remote_objnum];

	if (result < 0)
	{
		mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum));
		return(-1);
	}

#ifndef NDEBUG
	if (object_owner[result] != owner)
	{
		mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result]));
	}
#endif  
//      Assert(object_owner[result] == owner);

	return(result);
}

int objnum_local_to_remote(int local_objnum, byte *owner)
{
	// Map a local object number to a remote + owner

	int result;

	if ((local_objnum < 0) || (local_objnum > Highest_object_index)) {
		*owner = -1;
		return(-1);
	}

	*owner = object_owner[local_objnum];

	if (*owner == -1)
		return(local_objnum);

	if ((*owner >= N_players) || (*owner < -1)) {
		Int3(); // Illegal!
		*owner = -1;
		return local_objnum;
	}
	
	result = local_to_remote[local_objnum];
	
//      mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum,
//              *owner, result));

	if (result < 0)
	{
		Int3(); // See Rob, object has no remote number!
	}

	return(result);
}

void
map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner)
{
	// Add a mapping from a network remote object number to a local one

	Assert(local_objnum > -1);
	Assert(remote_objnum > -1);
	Assert(owner > -1);
	Assert(owner != Player_num);
	Assert(local_objnum < MAX_OBJECTS);
	Assert(remote_objnum < MAX_OBJECTS);

	object_owner[local_objnum] = owner;

	remote_to_local[owner][remote_objnum] = local_objnum;
	local_to_remote[local_objnum] = remote_objnum;

	return;
}

void
map_objnum_local_to_local(int local_objnum)
{
	// Add a mapping for our locally created objects

	Assert(local_objnum > -1);
	Assert(local_objnum < MAX_OBJECTS);

	object_owner[local_objnum] = Player_num;
	remote_to_local[Player_num][local_objnum] = local_objnum;
	local_to_remote[local_objnum] = local_objnum;

	return;
}

//
// Part 1 : functions whose main purpose in life is to divert the flow
//          of execution to either network or serial specific code based
//          on the curretn Game_mode value.
//

void
multi_endlevel_score(void)
{
	int old_connect,i;

	// Show a score list to end of net players

	// Save connect state and change to new connect state
#ifdef NETWORK
	if (Game_mode & GM_NETWORK)
	{
		old_connect = Players[Player_num].connected;
		if (Players[Player_num].connected!=3)
		 Players[Player_num].connected = CONNECT_END_MENU;
		Network_status = NETSTAT_ENDLEVEL;
		
	}
#endif

	// Do the actual screen we wish to show

	Function_mode = FMODE_MENU;

 	kmatrix_view(Game_mode & GM_NETWORK);

	Function_mode = FMODE_GAME;

	// Restore connect state

	if (Game_mode & GM_NETWORK)
	{
	  Players[Player_num].connected = old_connect;
	}

#ifndef SHAREWARE
	if (Game_mode & GM_MULTI_COOP)
	{
	   int i;
		for (i = 0; i < MaxNumNetPlayers; i++)
		// Reset keys
			Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY);
	}
	for (i = 0; i < MaxNumNetPlayers; i++)
	   Players[i].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear capture flag
	
#endif

  for (i=0;i<MAX_PLAYERS;i++)
    Players[i].KillGoalCount=0;

  for (i=0;i<MAX_POWERUP_TYPES;i++)
 	{
 		MaxPowerupsAllowed[i]=0;
 		PowerupsInMine[i]=0;
	}  

}

int
get_team(int pnum)
{
	if ((Game_mode & GM_CAPTURE) && ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
		return pnum;

	if (Netgame.team_vector & (1 << pnum))
		return 1;
	else
		return 0;
}

int
multi_choose_mission(int *anarchy_only)
{
	int i, n_missions;
	int default_mission;
   char *m[MAX_MISSIONS];
	int new_mission_num = 0;

	*anarchy_only = 0;

	n_missions = build_mission_list(1);

	if (n_missions > 1) {

		default_mission = 0;
		for (i=0;i<n_missions;i++) {
			m[i] = Mission_list[i].mission_name;
			if ( !stricmp( m[i], config_last_mission ) )    
				default_mission = i;
		}

      ExtGameStatus=GAMESTAT_START_MULTIPLAYER_MISSION;
		new_mission_num = newmenu_listbox1(TXT_MULTI_MISSION, n_missions, m, 1, default_mission, NULL );

		if (new_mission_num == -1)
			return -1;      //abort!

		strcpy(config_last_mission, m[new_mission_num]  );
		
		if (!load_mission(new_mission_num)) {
			nm_messagebox( NULL, 1, TXT_OK, TXT_MISSION_ERROR); 
			return -1;
		}

	   *anarchy_only = Mission_list[new_mission_num].anarchy_only_flag;
	}
	return(new_mission_num);
}

extern void game_disable_cheats();
	
void
multi_new_game(void)
{
	int i;
	
	// Reset variables for a new net game

	memset(kill_matrix, 0, MAX_NUM_NET_PLAYERS*MAX_NUM_NET_PLAYERS*2); // Clear kill matrix

	for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
	{
		sorted_kills[i] = i;
		Players[i].net_killed_total = 0;
		Players[i].net_kills_total = 0;
		Players[i].flags = 0;
		Players[i].KillGoalCount=0;
	}

#ifndef SHAREWARE
	for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
	{
		robot_controlled[i] = -1;       
		robot_agitation[i] = 0;
		robot_fired[i] = 0;
	}
#endif

	team_kills[0] = team_kills[1] = 0;
	Endlevel_sequence = 0;
	Player_is_dead = 0;
	multi_leave_menu = 0;
	multi_quit_game = 0;
	Show_kill_list = 1;
	game_disable_cheats();
	Player_exploded = 0;
	Dead_player_camera = 0;
}
	
void
multi_make_player_ghost(int playernum)
{
	object *obj;

//      Assert(playernum != Player_num);
//      Assert(playernum < MAX_NUM_NET_PLAYERS);

	if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS) || (playernum < 0))
	{
		Int3(); // Non-terminal, see Rob
		return;
	}

//      if (Objects[Players[playernum].objnum].type != OBJ_PLAYER)
//              mprintf((1, "Warning: Player %d is not currently a player.\n", playernum));

	obj = &Objects[Players[playernum].objnum];

	obj->type = OBJ_GHOST;
	obj->render_type = RT_NONE;
	obj->movement_type = MT_NONE;
	multi_reset_player_object(obj);

	if (Game_mode & GM_MULTI_ROBOTS)
		multi_strip_robots(playernum);
}

void
multi_make_ghost_player(int playernum)
{
	object *obj;

// Assert(playernum != Player_num);
// Assert(playernum < MAX_NUM_NET_PLAYERS);

	if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS))
	{
		Int3(); // Non-terminal, see rob
		return;
	}

//      if(Objects[Players[playernum].objnum].type != OBJ_GHOST)
//              mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum));

	obj = &Objects[Players[playernum].objnum];

	obj->type = OBJ_PLAYER;
	obj->movement_type = MT_PHYSICS;
	multi_reset_player_object(obj);
}

int multi_get_kill_list(int *plist)
{
	// Returns the number of active net players and their
	// sorted order of kills
	int i;
	int n = 0;

	for (i = 0; i < N_players; i++)
//              if (Players[sorted_kills[i]].connected)
			plist[n++] = sorted_kills[i];

	if (n == 0)
		Int3(); // SEE ROB OR MATT

//      memcpy(plist, sorted_kills, N_players*sizeof(int));     

	return(n);
}
	
void
multi_sort_kill_list(void)
{
	// Sort the kills list each time a new kill is added

	int kills[MAX_NUM_NET_PLAYERS];
	int i;
	int changed = 1;
	
	for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
	{
#ifndef SHAREWARE
		if (Game_mode & GM_MULTI_COOP)
			kills[i] = Players[i].score;
		else
#endif
		if (Show_kill_list==2)
		 {
		  if (Players[i].net_killed_total+Players[i].net_kills_total==0)
			kills[i]=-1;  // always draw the ones without any ratio last
		  else
		   kills[i]=(int)((float)((float)Players[i].net_kills_total/((float)Players[i].net_killed_total+(float)Players[i].net_kills_total))*100.0);		
		 }	
		else			
			kills[i] = Players[i].net_kills_total;
	}

	while (changed)
	{
		changed = 0;
		for (i = 0; i < N_players-1; i++)
		{
			if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]])
			{
				changed = sorted_kills[i];
				sorted_kills[i] = sorted_kills[i+1];
				sorted_kills[i+1] = changed;
				changed = 1;
			}
		}
	}
      mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1]));
}

extern object *obj_find_first_of_type (int);
char Multi_killed_yourself=0;

void multi_compute_kill(int killer, int killed)
{
	// Figure out the results of a network kills and add it to the
	// appropriate player's tally.

	int killed_pnum, killed_type;
	int killer_pnum, killer_type,killer_id;
	int TheGoal;
	char killed_name[(CALLSIGN_LEN*2)+4];
	char killer_name[(CALLSIGN_LEN*2)+4];

	kmatrix_kills_changed = 1;
   Multi_killed_yourself=0;
  
     	// Both object numbers are localized already!

	mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed));

	if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index))
	{
		Int3(); // See Rob, illegal value passed to compute_kill;
		return;
	}

	killed_type = Objects[killed].type;
	killer_type = Objects[killer].type;
   killer_id = Objects[killer].id;

	if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST)) 
	{
		Int3(); // compute_kill passed non-player object!
		return;
	}

	killed_pnum = Objects[killed].id;

   PKilledFlags[killed_pnum]=1;

	Assert ((killed_pnum >= 0) && (killed_pnum < N_players));

	if (Game_mode & GM_TEAM)
		sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]);
	else
		sprintf(killed_name, "%s", Players[killed_pnum].callsign);

	if (Newdemo_state == ND_STATE_RECORDING)
		newdemo_record_multi_death(killed_pnum);

	digi_play_sample( SOUND_HUD_KILL, F3_0 );

   if (Control_center_destroyed)
	 Players[killed_pnum].connected=3;

	if (killer_type == OBJ_CNTRLCEN)
	{
		Players[killed_pnum].net_killed_total++;
		Players[killed_pnum].net_kills_total--;

		if (Newdemo_state == ND_STATE_RECORDING)
			newdemo_record_multi_kill(killed_pnum, -1);

		if (killed_pnum == Player_num)
		 {
			HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY);
			multi_add_lifetime_killed ();
		 }
		else
			HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY );
		return;         
	}

#ifndef SHAREWARE
	else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
	{
	 	if (killer_id==PMINE_ID && killer_type!=OBJ_ROBOT)
	    {
		  if (killed_pnum == Player_num) 
				HUD_init_message("You were killed by a mine!");
			else
				HUD_init_message("%s was killed by a mine!",killed_name);
		 }
		else
	    {
		  if (killed_pnum == Player_num) 
			 {
				HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT);	
				multi_add_lifetime_killed();
			 }
	     else
				HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT );
       }
		Players[killed_pnum].net_killed_total++;
		return;         
	}
#else
	else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST) && (killer_id!=PMINE_ID))
	{
		Int3(); // Illegal killer type?
		return;
	}
  if (killer_id==PMINE_ID)
   {
    if (killed_pnum==Player_num)
	   HUD_init_message("You were killed by a mine!");
    else
		HUD_init_message("%s was killed by a mine!",killed_name);
	 
    Players[killed_pnum].net_killed_total++;
	
    return;
   }
#endif

	killer_pnum = Objects[killer].id;

	if (Game_mode & GM_TEAM)
		sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]);
	else
		sprintf(killer_name, "%s", Players[killer_pnum].callsign);

	// Beyond this point, it was definitely a player-player kill situation

	if ((killer_pnum < 0) || (killer_pnum >= N_players))
		Int3(); // See rob, tracking down bug with kill HUD messages
	if ((killed_pnum < 0) || (killed_pnum >= N_players))
		Int3(); // See rob, tracking down bug with kill HUD messages

	if (killer_pnum == killed_pnum)
	{
      if (!(Game_mode & GM_HOARD))
		{
			if (Game_mode & GM_TEAM)
				team_kills[get_team(killed_pnum)] -= 1;

			Players[killed_pnum].net_killed_total += 1;
			Players[killed_pnum].net_kills_total -= 1;

			if (Newdemo_state == ND_STATE_RECORDING)
				newdemo_record_multi_kill(killed_pnum, -1);
		}
		kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides

		if (killer_pnum == Player_num)
		 {
			HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF );	
		   Multi_killed_yourself=1;
			multi_add_lifetime_killed();
		 }
		else
			HUD_init_message("%s %s", killed_name, TXT_SUICIDE);
	}

	else
	{
		if (!(Game_mode & GM_HOARD))
		{
			if (Game_mode & GM_TEAM)
			{
				if (get_team(killed_pnum) == get_team(killer_pnum))
					team_kills[get_team(killed_pnum)] -= 1;
				else
					team_kills[get_team(killer_pnum)] += 1;
			}

			Players[killer_pnum].net_kills_total += 1;
			Players[killer_pnum].KillGoalCount+=1;

			if (Newdemo_state == ND_STATE_RECORDING)
				newdemo_record_multi_kill(killer_pnum, 1);
		}
		else
		{
			if (Game_mode & GM_TEAM)
			{
				if (killed_pnum==Player_num && get_team(killed_pnum) == get_team(killer_pnum))
					Multi_killed_yourself=1;
			}
		}
	
		kill_matrix[killer_pnum][killed_pnum] += 1;
		Players[killed_pnum].net_killed_total += 1;
		if (killer_pnum == Player_num) {
			HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, killed_name);
			multi_add_lifetime_kills();
			if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000))
				add_points_to_score(-1000);
		}
		else if (killed_pnum == Player_num)
		  {
			HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, TXT_YOU);
			multi_add_lifetime_killed();
			if (Game_mode & GM_HOARD) 
			 {
				if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>3)
					multi_send_play_by_play (1,killer_pnum,Player_num);
				else if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>0)
					multi_send_play_by_play (0,killer_pnum,Player_num);
		 	 }
		  }
		else
			HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, killed_name);
	}

  TheGoal=Netgame.KillGoal*5;

  if (Netgame.KillGoal>0)
	{
	 if (Players[killer_pnum].KillGoalCount>=TheGoal)
	  {
	   if (killer_pnum==Player_num)
	    {
	     HUD_init_message("You reached the kill goal!");
	     Players[Player_num].shields=i2f(200);
	    }  
	   else
	    HUD_init_message ("%s has reached the kill goal!",Players[killer_pnum].callsign);

	   HUD_init_message ("The control center has been destroyed!");
      net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
	  }
	}

	multi_sort_kill_list();
	multi_show_player_list();
   Players[killed_pnum].flags&=(~(PLAYER_FLAGS_HEADLIGHT_ON));  // clear the killed guys flags/headlights
}

void
multi_do_frame(void)
{
     static int lasttime=0;
     int i;

	if (!(Game_mode & GM_MULTI))
	{
		Int3();
		return;
	}

  if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime))
	{
	 for (i=0;i<N_players;i++)
	  if (Players[i].connected)
	   {
	    if (i==Player_num)
	     {
	      multi_send_heartbeat();
	      lasttime=f2i(ThisLevelTime);
	     }
	    break;
	   }
	}

	multi_send_message(); // Send any waiting messages

	if (!multi_in_menu)
		multi_leave_menu = 0;

#ifndef SHAREWARE
	if (Game_mode & GM_MULTI_ROBOTS)
	{
		multi_check_robot_timeout();
	}
#endif  

	if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
	{
		com_do_frame();
	}
	else
	{
		network_do_frame(0, 1);
	}

	if (multi_quit_game && !multi_in_menu)
	{
		multi_quit_game = 0;
		longjmp(LeaveGame, 0);
	}
}
		
void
multi_send_data(char *buf, int len, int repeat)
{
	Assert(len == message_length[buf[0]]);
	Assert(buf[0] <= MULTI_MAX_TYPE);
//      Assert(buf[0] >= 0);

	if (Game_mode & GM_NETWORK)
		Assert(buf[0] > 0);

	if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
		com_send_data(buf, len, repeat);
	else if (Game_mode & GM_NETWORK)
		network_send_data(buf, len, repeat);
}

extern void AdjustMineSpawn();

void
multi_leave_game(void)
{

//      if (Function_mode != FMODE_GAME)
//              return;

	if (!(Game_mode & GM_MULTI))
		return;

	if (Game_mode & GM_NETWORK)
	{
		mprintf((0, "Sending explosion message.\n"));

		Net_create_loc = 0;
		AdjustMineSpawn();
		multi_cap_objects();
		drop_player_eggs(ConsoleObject);
		multi_send_position(Players[Player_num].objnum);
		multi_send_player_explode(MULTI_PLAYER_DROP);
	}

	mprintf((1, "Sending leave game.\n"));
	multi_send_quit(MULTI_QUIT);

	if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
		serial_leave_game();
	if (Game_mode & GM_NETWORK)
		network_leave_game();

	Game_mode |= GM_GAME_OVER;

   if (Function_mode!=FMODE_EXIT)
		Function_mode = FMODE_MENU;

//      N_players = 0;

//      change_playernum_to(0);
//      Viewer = ConsoleObject = &Objects[0];

}
		
void 
multi_show_player_list()
{
	if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP))
		return;

	if (Show_kill_list)
		return;

	Show_kill_list_timer = F1_0*5; // 5 second timer
	Show_kill_list = 1;
}

int 
multi_endlevel(int *secret)
{
	int result = 0;

	if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
		com_endlevel(secret);          // an opportunity to re-sync or whatever
	else if (Game_mode & GM_NETWORK)
		result = network_endlevel(secret);
	
	return(result);         
}

//
// Part 2 : functions that act on network/serial messages and change the
//          the state of the game in some way.
//

#ifndef MACINTOSH
extern PORT *com_port;
#endif

int 
multi_menu_poll(void)
{
	fix old_shields;
	int was_fuelcen_alive;
	int player_was_dead;

	was_fuelcen_alive = Control_center_destroyed;

	// Special polling function for in-game menus for multiplayer and serial

	if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) )
		return(0);

	if (multi_leave_menu)
		return(-1);

	old_shields = Players[Player_num].shields;
	player_was_dead = Player_is_dead;

	multi_in_menu++; // Track level of menu nesting

	GameLoop( 0, 0 );                       

	multi_in_menu--;

//	timer_delay(f1_0/10);   changed by allender for portability
	delay(100);				// delay 100 milliseconds
		
	if (Endlevel_sequence || (Control_center_destroyed && !was_fuelcen_alive) || (Player_is_dead != player_was_dead) || (Players[Player_num].shields < old_shields))
	{
		multi_leave_menu = 1;
		return(-1);
	}
	if ((Control_center_destroyed) && (Countdown_seconds_left < 10))
	{
		multi_leave_menu = 1;
		return(-1);
	}

#if !defined(WINDOWS) && !defined(MACINTOSH)
	if ((Game_mode & GM_MODEM) && (!GetCd(com_port)))
	{
		multi_leave_menu = 1;
		return(-1);
	}
#endif

	return(0);
}

void
multi_define_macro(int key)
{
	if (!(Game_mode & GM_MULTI))
		return;

	key &= (~KEY_SHIFTED);

	switch(key) 
	{
		case KEY_F9:
			multi_defining_message = 1; break;
		case KEY_F10:
			multi_defining_message = 2; break;
		case KEY_F11:
			multi_defining_message = 3; break;
		case KEY_F12:
			multi_defining_message = 4; break;
		default:
			Int3();
	}

	if (multi_defining_message)     {
		multi_message_index = 0;
		Network_message[multi_message_index] = 0;
	}

}

char feedback_result[200];

void
multi_message_feedback(void)
{
	char *colon;
	int found = 0;
	int i;

	if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) ))
	{
		sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO);
		if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3))
		{
			sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]);
			found = 1;
		}
		if (Game_mode & GM_TEAM)
		{
			for (i = 0; i < N_players; i++)
			{
				if (!strnicmp(Netgame.team_name[i], Network_message, colon-Network_message))
				{
					if (found)
						strcat(feedback_result, ", ");
					found++;
					if (!(found % 4))
						strcat(feedback_result, "\n");
					sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]);
				}
			}
		}
		for (i = 0; i < N_players; i++)
		{
			if ((!strnicmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected))
			{
				if (found)
					strcat(feedback_result, ", ");
				found++;
				if (!(found % 4))
					strcat(feedback_result, "\n");
				sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign);
			}
		}
		if (!found)
			strcat(feedback_result, TXT_NOBODY);
		else
			strcat(feedback_result, ".");

		digi_play_sample(SOUND_HUD_MESSAGE, F1_0);

		Assert(strlen(feedback_result) < 200);

		HUD_init_message(feedback_result);
//      sprintf (temp,"%s",colon);
//	   sprintf (Network_message,"%s",temp);
 
	}
}

void
multi_send_macro(int key)
{
	if (! (Game_mode & GM_MULTI) )
		return;

	switch(key) 
	{
		case KEY_F9:
			key = 0; break;
		case KEY_F10:
			key = 1; break;
		case KEY_F11:
			key = 2; break;
		case KEY_F12:
			key = 3; break;
		default:
			Int3();
	}

	if (!Network_message_macro[key][0])
	{
		HUD_init_message(TXT_NO_MACRO);
		return;
	}

	strcpy(Network_message, Network_message_macro[key]);
	Network_message_reciever = 100;

	HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
	multi_message_feedback();
}


void 
multi_send_message_start()
{
	if (Game_mode&GM_MULTI) {
		multi_sending_message = 1;
		multi_message_index = 0;
		Network_message[multi_message_index] = 0;
	}
}

extern fix StartingShields;
fix PingLaunchTime,PingReturnTime;

extern void network_send_ping (ubyte);
extern network_dump_player(ubyte * server, ubyte *node, int why);
extern int network_who_is_master();
extern void network_send_netgame_update();
extern char NameReturning;
extern int force_cockpit_redraw;

void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why);

void multi_send_message_end()
{
	char *mytempbuf;
   int i,t;
	
	Network_message_reciever = 100;

	if (!strnicmp (Network_message,"!Names",6))
		{
		 NameReturning=1-NameReturning;
		 HUD_init_message ("Name returning is now %s.",NameReturning?"active":"disabled");
		}
	else if (!strnicmp (Network_message,"Handicap:",9))
		{
		 mytempbuf=&Network_message[9];		 
		 mprintf ((0,"Networkhandi=%s\n",mytempbuf));
		 StartingShields=atol (mytempbuf);
		 if (StartingShields<10)
			StartingShields=10;
		 if (StartingShields>100)
			{
				sprintf (Network_message,"%s has tried to cheat!",Players[Player_num].callsign);
				StartingShields=100;
			}
	 	 else
			sprintf (Network_message,"%s handicap is now %d",Players[Player_num].callsign,StartingShields);

		 HUD_init_message ("Telling others of your handicap of %d!",StartingShields);
		 StartingShields=i2f(StartingShields);
		}
	else if (!strnicmp (Network_message,"NoBombs",7))
	 Netgame.DoSmartMine=0;
	else if (!strnicmp (Network_message,"Ping:",5))
	 {
	  if (Game_mode & GM_NETWORK)
		{
			int name_index=5;
			if (strlen(Network_message) > 5)
				while (Network_message[name_index] == ' ')
					name_index++;

			if (strlen(Network_message)<=name_index)
			 {
				HUD_init_message ("You must specify a name to ping");
				return;
			 }

			for (i = 0; i < N_players; i++)
  	        if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected))
            {
				 PingLaunchTime=timer_get_fixed_seconds();
				 network_send_ping (i);
				 HUD_init_message("Pinging %s...",Players[i].callsign);
			    multi_message_index = 0;
				 multi_sending_message = 0;
				 return;
			   }
		}
	  else // Modem/Serial ping
	   {
			 PingLaunchTime=timer_get_fixed_seconds();
			 multi_send_modem_ping ();
			 HUD_init_message("Pinging opponent...");
		    multi_message_index = 0;
			 multi_sending_message = 0;
			 return;
	   }
	 }												 
	else if (!strnicmp (Network_message,"move:",5))
	 { 
		mprintf ((0,"moving?\n"));
		
	  if ((Game_mode & GM_NETWORK) && (Game_mode & GM_TEAM))
		{
			int name_index=5;
			if (strlen(Network_message) > 5)
				while (Network_message[name_index] == ' ')
					name_index++;

		  if (!network_i_am_master())
			{	
			 HUD_init_message ("Only %s can move players!",Players[network_who_is_master()].callsign);
			 return;
			}
	
			if (strlen(Network_message)<=name_index)
			 {
				HUD_init_message ("You must specify a name to move");
				return;
			 }

			for (i = 0; i < N_players; i++)
  	        if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (Players[i].connected))
            {
				 if ((Game_mode & GM_CAPTURE) && (Players[i].flags & PLAYER_FLAGS_FLAG))
				  {
					HUD_init_message ("Can't move player because s/he has a flag!");
					return;
				  }
		
				 if (Netgame.team_vector & (1<<i))
				   Netgame.team_vector&=(~(1<<i));
				 else
				   Netgame.team_vector|=(1<<i);
	
		  		 for (t=0;t<N_players;t++)
				 if (Players[t].connected)
		   	 	multi_reset_object_texture (&Objects[Players[t].objnum]);

				 network_send_netgame_update ();	
				 sprintf (Network_message,"%s has changed teams!",Players[i].callsign);
				 if (i==Player_num)
					{
				    HUD_init_message ("You have changed teams!");
					 reset_cockpit(); 	
					}
				 else
					 HUD_init_message ("Moving %s to other team.",Players[i].callsign);
				 break;
			   }
	 	  }    
	 }												 

	else if (!strnicmp (Network_message,"kick:",5) && (Game_mode & GM_NETWORK))
	 {
		int name_index=5;
		if (strlen(Network_message) > 5)
			while (Network_message[name_index] == ' ')
				name_index++;

	  if (!network_i_am_master())
		{	
		 HUD_init_message ("Only %s can kick others out!",Players[network_who_is_master()].callsign);
	    multi_message_index = 0;
 	    multi_sending_message = 0;
		 return;
		}
		if (strlen(Network_message)<=name_index)
		 {
			HUD_init_message ("You must specify a name to kick");
			multi_message_index = 0;
			multi_sending_message = 0;
			return;
		 }

		if (Network_message[name_index] == '#' && isdigit(Network_message[name_index+1])) {
			int players[MAX_NUM_NET_PLAYERS];
			int listpos = Network_message[name_index+1] - '0';

			mprintf ((0,"Trying to kick %d , show_kill_list=%d\n",listpos,Show_kill_list));

			if (Show_kill_list==1 || Show_kill_list==2) {
				if (listpos == 0 || listpos >= N_players) {
					HUD_init_message ("Invalid player number for kick.");
				   multi_message_index = 0;
			 	   multi_sending_message = 0;
				  	return;
				}
				multi_get_kill_list(players);
				i = players[listpos];
				if ((i != Player_num) && (Players[i].connected))
					goto kick_player;
			}
			else HUD_init_message ("You cannot use # kicking with in team display.");
				

		    multi_message_index = 0;
 		    multi_sending_message = 0;
		  	 return;
		}


		for (i = 0; i < N_players; i++)
	    	if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected)) {
kick_player:;
  	   		if (Network_game_type == IPX_GAME)
					network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, 7);
				else
					network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, 7);
				
				HUD_init_message("Dumping %s...",Players[i].callsign);
				multi_message_index = 0;
				multi_sending_message = 0;
				return;
			}
	 }												 

   else
		HUD_init_message("%s '%s'", TXT_SENDING, Network_message);

	multi_send_message();
	multi_message_feedback();               

	multi_message_index = 0;
	multi_sending_message = 0;
}

void multi_define_macro_end()
{
	Assert( multi_defining_message > 0 );

	strcpy( Network_message_macro[multi_defining_message-1], Network_message );
	write_player_file();

	multi_message_index = 0;
	multi_defining_message = 0;
}

void multi_message_input_sub( int key )
{
	switch( key )   {
	case KEY_F8:
	case KEY_ESC:
		multi_sending_message = 0;
		multi_defining_message = 0;
		game_flush_inputs();
		break;
	case KEY_LEFT:
	case KEY_BACKSP:
	case KEY_PAD4:
		if (multi_message_index > 0)
			multi_message_index--;
		Network_message[multi_message_index] = 0;
		break;
	case KEY_ENTER:
		if ( multi_sending_message )    
			multi_send_message_end();
		else if ( multi_defining_message )
			multi_define_macro_end();
		game_flush_inputs();
		break;
	default:
		if ( key > 0 )  {
			int ascii = key_to_ascii(key);
			if ((ascii < 255 ))     {
				if (multi_message_index < MAX_MESSAGE_LEN-2 )   {
					Network_message[multi_message_index++] = ascii;
					Network_message[multi_message_index] = 0;
				} else if ( multi_sending_message )     {               
					int i;
					char * ptext, * pcolon;
					ptext = NULL;
					Network_message[multi_message_index++] = ascii;
					Network_message[multi_message_index] = 0;
					for (i=multi_message_index-1; i>=0; i-- )       {
						if ( Network_message[i]==32 )   {
							ptext = &Network_message[i+1];
							Network_message[i] = 0;
							break;
						}
					}
					multi_send_message_end();
					if ( ptext )    {
						multi_sending_message = 1;
						pcolon = strchr( Network_message, ':' );
						if ( pcolon )
							strcpy( pcolon+1, ptext );
						else
							strcpy( Network_message, ptext );
						multi_message_index = strlen( Network_message );
					} 
				}
			}
		}
	}
}

void 
multi_send_message_dialog(void)
{
	newmenu_item m[1];
	int choice;

	if (!(Game_mode&GM_MULTI))
		return;

	Network_message[0] = 0;             // Get rid of old contents

	m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1;
	choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL );

	if ((choice > -1) && (strlen(Network_message) > 0)) {
		Network_message_reciever = 100;
		HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
		multi_message_feedback();               
	}
}



void
multi_do_death(int objnum)
{
	// Do any miscellaneous stuff for a new network player after death

	objnum = objnum;

	if (!(Game_mode & GM_MULTI_COOP)) 
	{
		mprintf((0, "Setting all keys for player %d.\n", Player_num));
		Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
	}
}

void
multi_do_fire(char *buf)
{
	ubyte weapon;
	char pnum;
	byte flags;
   //static dum=0;
	
	// Act out the actual shooting
	pnum = buf[1];

#ifndef MACINTOSH
	weapon = (int)buf[2];   
#else
	weapon = buf[2];
#endif
	flags = buf[4];
	Network_laser_track = INTEL_SHORT(*(short *)(buf+6));
	
	Assert (pnum < N_players);

	if (Objects[Players[pnum].objnum].type == OBJ_GHOST)
		multi_make_ghost_player(pnum);

   //  mprintf((0,"multi_do_fire, weapon = %d\n",weapon));

	if (weapon == FLARE_ADJUST)
		Laser_player_fire( Objects+Players[pnum].objnum, FLARE_ID, 6, 1, 0);
	else if (weapon >= MISSILE_ADJUST) {
		int weapon_id,weapon_gun;

		weapon_id = Secondary_weapon_to_weapon_info[weapon-MISSILE_ADJUST];
		weapon_gun = Secondary_weapon_to_gun_num[weapon-MISSILE_ADJUST] + (flags & 1);
		mprintf((0,"missile id = %d, gun = %d\n",weapon_id,weapon_gun));

		if (weapon-MISSILE_ADJUST==GUIDED_INDEX)
		 {
		  mprintf ((0,"Missile is guided!!!\n"));
		  Multi_is_guided=1;
		 }

		Laser_player_fire( Objects+Players[pnum].objnum, weapon_id, weapon_gun, 1, 0 );
	}
	else {
		fix save_charge = Fusion_charge;

		if (weapon == FUSION_INDEX) {
			Fusion_charge = flags << 12;
			mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
		}
		if (weapon == LASER_ID) {
			if (flags & LASER_QUAD)
				Players[pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
			else
				Players[pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
		}
	
		do_laser_firing(Players[pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
	
		if (weapon == FUSION_INDEX)
			Fusion_charge = save_charge;
	}
}

void 
multi_do_message(char *buf)
{
	char *colon;
   char *tilde,mesbuf[100];
   int tloc,t;

	int loc = 2;
	
   if (tilde=strchr (buf+loc,'$'))  // do that stupid name stuff
	 {											// why'd I put this in?  Probably for the
	  tloc=tilde-(buf+loc);				// same reason you can name your guidebot
	  mprintf ((0,"Tloc=%d\n",tloc));
   
	  if (tloc>0)
   	  strncpy (mesbuf,buf+loc,tloc);
	  strcpy (mesbuf+tloc,Players[Player_num].callsign);
     strcpy (mesbuf+strlen(Players[Player_num].callsign)+tloc,buf+loc+tloc+1);
     strcpy (buf+loc,mesbuf);
	 }	
 
	if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
	{
	   mesbuf[0] = 1;
		mesbuf[1] = BM_XRGB(28, 0, 0);
		strcpy(&mesbuf[2], Players[buf[1]].callsign);
		t = strlen(mesbuf);
		mesbuf[t] = ':';
		mesbuf[t+1] = 1;
		mesbuf[t+2] = BM_XRGB(0, 31, 0);
		mesbuf[t+3] = 0;

		digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
		HUD_init_message("%s %s", mesbuf, buf+2);
	}
	else
	{
		if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
			  ((Game_mode & GM_TEAM) && ( (get_team(Player_num) == atoi(buf+loc)-1) || !strnicmp(Netgame.team_name[get_team(Player_num)], buf+loc, colon-(buf+loc)))) )
		{
		   mesbuf[0] = 1;
			mesbuf[1] = BM_XRGB(0, 32, 32);
			strcpy(&mesbuf[2], Players[buf[1]].callsign);
			t = strlen(mesbuf);
			mesbuf[t] = ':';
			mesbuf[t+1] = 1;
			mesbuf[t+2] = BM_XRGB(0, 31, 0);
			mesbuf[t+3] = 0;

			digi_play_sample(SOUND_HUD_MESSAGE, F1_0);      
   		HUD_init_message("%s %s", mesbuf, colon+1);
		}
	}
}

void
multi_do_position(char *buf)
{               
#ifdef MACINTOSH
	shortpos sp;
#endif

	// This routine does only player positions, mode game only
	//      mprintf((0, "Got position packet.\n"));

	int pnum = (Player_num+1)%2;

	Assert(&Objects[Players[pnum].objnum] != ConsoleObject);

	if (Game_mode & GM_NETWORK)
	 {
	  Int3(); // Get Jason, what the hell are we doing here?
	  return;
    }
	 

#ifndef MACINTOSH
	extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1),0);
#else
	memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + 1), 9);
	memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + 10), 14);
	extract_shortpos(&Objects[Players[pnum].objnum], &sp, 1);
#endif

	if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
		set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
}

void
multi_do_reappear(char *buf)
{
	short objnum;

	objnum = INTEL_SHORT(*(short *)(buf+1));

	Assert(objnum >= 0);
//      Assert(Players[Objects[objnum].id]].objnum == objnum);
	
// mprintf((0, "Switching rendering back on for object %d.\n", objnum));

	multi_make_ghost_player(Objects[objnum].id);
	create_player_appearance_effect(&Objects[objnum]);
	PKilledFlags[Objects[objnum].id]=0;
}
	
void
multi_do_player_explode(char *buf)
{
	// Only call this for players, not robots.  pnum is player number, not
	// Object number.

	object *objp;
	int count;
	int pnum;
	int i;
	char remote_created;

	pnum = buf[1];

#ifdef NDEBUG
	if ((pnum < 0) || (pnum >= N_players))
		return;
#else
	Assert(pnum >= 0);
	Assert(pnum < N_players);
#endif

#ifdef NETWORK
	// If we are in the process of sending objects to a new player, reset that process
	if (Network_send_objects)
	{
		mprintf((0, "Resetting object sync due to player explosion.\n"));
		Network_send_objnum = -1;
	}
#endif

	// Stuff the Players structure to prepare for the explosion
	
	count = 2;
	Players[pnum].primary_weapon_flags = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
	Players[pnum].secondary_weapon_flags = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
	Players[pnum].laser_level = buf[count];                                                 count++;
	Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count];                count++;
	Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
	Players[pnum].secondary_ammo[SMART_INDEX] = buf[count];         count++;
	Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count];          count++;
	Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;

	Players[pnum].secondary_ammo[SMISSILE1_INDEX] = buf[count]; count++;
	Players[pnum].secondary_ammo[GUIDED_INDEX]    = buf[count]; count++;
	Players[pnum].secondary_ammo[SMART_MINE_INDEX]= buf[count]; count++;
	Players[pnum].secondary_ammo[SMISSILE4_INDEX] = buf[count]; count++;
	Players[pnum].secondary_ammo[SMISSILE5_INDEX] = buf[count]; count++;

	Players[pnum].primary_ammo[VULCAN_INDEX] = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
	Players[pnum].primary_ammo[GAUSS_INDEX] = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
	Players[pnum].flags = INTEL_INT(*(uint *)(buf+count));                     count += 4;
    
	multi_adjust_remote_cap (pnum);

	objp = Objects+Players[pnum].objnum;

//      objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
//      objp->pos = *(vms_vector *)(buf+28);                // 12 bytes

	remote_created = buf[count++]; // How many did the other guy create?

	Net_create_loc = 0;

	drop_player_eggs(objp);
 
	// Create mapping from remote to local numbering system

	mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));

	// We now handle this situation gracefully, Int3 not required
	//      if (Net_create_loc != remote_created)
	//              Int3(); // Probably out of object array space, see Rob

	for (i = 0; i < remote_created; i++)
	{
		short s;
		
		s = INTEL_SHORT(*(short *)(buf+count));
		
		if ((i < Net_create_loc) && (s > 0))
			map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);          
		else if (*(short *)(buf+count) <= 0)
		{
			mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", s, pnum));
		}
		else 
		{
			mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
		}
//              Assert(s > 0);
		count += 2;
	}
	for (i = remote_created; i < Net_create_loc; i++) {
		mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
		Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
	}

	if (buf[0] == MULTI_PLAYER_EXPLODE)
	{
		explode_badass_player(objp);
		
		objp->flags &= ~OF_SHOULD_BE_DEAD;              //don't really kill player
		multi_make_player_ghost(pnum);
	}
	else
	{
		create_player_appearance_effect(objp);
	}

	Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE | PLAYER_FLAGS_FLAG);
	Players[pnum].cloak_time = 0;
}

void
multi_do_kill(char *buf)
{
	int killer, killed;
	int count = 1;
	int pnum;
	
	pnum = (int)(buf[count]);
	if ((pnum < 0) || (pnum >= N_players))
	{
		Int3(); // Invalid player number killed
		return;
	}
	killed = Players[pnum].objnum;                  
	count += 1;

	killer = INTEL_SHORT(*(short *)(buf+count)); 
	if (killer > 0)
		killer = objnum_remote_to_local(killer, (byte)buf[count+2]);

#ifdef SHAREWARE
	if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
	{
		Int3();
		mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
		return;
	}
#endif          

	multi_compute_kill(killer, killed);

}


//      Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
// which means not a controlcen object, but contained in another object
void multi_do_controlcen_destroy(char *buf)
{
	byte who;
	short objnum;

	objnum = INTEL_SHORT(*(short *)(buf+1));
	who = buf[3];

	if (Control_center_destroyed != 1) 
	{
		if ((who < N_players) && (who != Player_num)) {
			HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
		}
		else if (who == Player_num)
			HUD_init_message(TXT_YOU_DEST_CONTROL);
		else 
			HUD_init_message(TXT_CONTROL_DESTROYED);

		if (objnum != -1)
			net_destroy_controlcen(Objects+objnum);
		else
			net_destroy_controlcen(NULL);
	}
}

void 
multi_do_escape(char *buf)
{
	int objnum;

	objnum = Players[buf[1]].objnum;

	digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
	digi_kill_sound_linked_to_object (objnum);
	
	if (buf[2] == 0)
	{
		HUD_init_message("%s %s", Players[buf[1]].callsign, TXT_HAS_ESCAPED);
		if (Game_mode & GM_NETWORK)
			Players[buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
		if (!multi_goto_secret)
			multi_goto_secret = 2;
	}
	else if (buf[2] == 1) 
	{
		HUD_init_message("%s %s", Players[buf[1]].callsign, TXT_HAS_FOUND_SECRET);
		if (Game_mode & GM_NETWORK)
			Players[buf[1]].connected = CONNECT_FOUND_SECRET;
		if (!multi_goto_secret)
			multi_goto_secret = 1;
	}
	create_player_appearance_effect(&Objects[objnum]);
	multi_make_player_ghost(buf[1]);
}

void
multi_do_remobj(char *buf)
{
	short objnum; // which object to remove
	short local_objnum;
	byte obj_owner; // which remote list is it entered in

	objnum = INTEL_SHORT(*(short *)(buf+1));
	obj_owner = buf[3];

	Assert(objnum >= 0);

	if (objnum < 1)
		return;

	local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum

//      mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));

	if (local_objnum < 0)
	{
		mprintf((1, "multi_do_remobj: Could not remove referenced object.\n"));
		return;
	}

	if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
	{
		mprintf((1, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
		return;
	}
	
	if (Network_send_objects && network_objnum_is_past(local_objnum))
	{
		mprintf((0, "Resetting object sync due to object removal.\n"));
		Network_send_objnum = -1;
	}
	if (Objects[local_objnum].type==OBJ_POWERUP)
	 if (Game_mode & GM_NETWORK)	
		{
	  	 if (PowerupsInMine[Objects[local_objnum].id]>0)
			PowerupsInMine[Objects[local_objnum].id]--;

		  if (multi_powerup_is_4pack (Objects[local_objnum].id))
			 {
				mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
		
				if (PowerupsInMine[Objects[local_objnum].id-1]-4<0)
					PowerupsInMine[Objects[local_objnum].id-1]=0;
				else
					PowerupsInMine[Objects[local_objnum].id-1]-=4;
			 }
		
		 mprintf ((0,"Decrementing powerups! %d\n",PowerupsInMine[Objects[local_objnum].id]));
		}

	Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
	
}

void
multi_do_quit(char *buf)
{
	
	if (Game_mode & GM_NETWORK)
	{
		int i, n = 0;

		digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );

		HUD_init_message( "%s %s", Players[buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
		
		network_disconnect_player(buf[1]);

		if (multi_in_menu)
			return;

		for (i = 0; i < N_players; i++)
			if (Players[i].connected) n++;
		if (n == 1)
		{
			nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
		}
	}

	if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
	{
		Function_mode = FMODE_MENU;
		multi_quit_game = 1;
		multi_leave_menu = 1;
		nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
		Function_mode = FMODE_GAME;
		multi_reset_stuff();
	}
	return;
}

void
multi_do_cloak(char *buf)
{
	int pnum;

	pnum = (int)(buf[1]);

	Assert(pnum < N_players);
	
	mprintf((0, "Cloaking player %d\n", pnum));

	Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
	Players[pnum].cloak_time = GameTime;
	ai_do_cloak_stuff();

#ifndef SHAREWARE
	if (Game_mode & GM_MULTI_ROBOTS)
		multi_strip_robots(pnum);
#endif

	if (Newdemo_state == ND_STATE_RECORDING)
		newdemo_record_multi_cloak(pnum);
}
	
void
multi_do_decloak(char *buf)
{
	int pnum;

	pnum = (int)(buf[1]);

	if (Newdemo_state == ND_STATE_RECORDING)
		newdemo_record_multi_decloak(pnum);

}
	
void
multi_do_door_open(char *buf)
{
	int segnum;
	byte side;
	segment *seg;
	wall *w;
	ubyte flag;

	segnum = INTEL_SHORT(*(short *)(buf+1));
	side = buf[3];
	flag= buf[4];

//      mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));

	if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
	{
		Int3();
		return;
	}

	seg = &Segments[segnum];

	if (seg->sides[side].wall_num == -1) {  //Opening door on illegal wall
		Int3();
		return;
	}

	w = &Walls[seg->sides[side].wall_num];

	if (w->type == WALL_BLASTABLE)
	{
		if (!(w->flags & WALL_BLASTED))
		{
			mprintf((0, "Blasting wall by remote command.\n"));
			wall_destroy(seg, side);
		}
		return;
	}
	else if (w->state != WALL_DOOR_OPENING)
	{
		wall_open_door(seg, side);
      w->flags=flag;
	}
	else
    w->flags=flag;

//      else
//              mprintf((0, "Door already opening!\n"));
}

void
multi_do_create_explosion(char *buf)
{
	int pnum;
	int count = 1;

	pnum = buf[count++];

//      mprintf((0, "Creating small fireball.\n"));
	create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
}
	
void
multi_do_controlcen_fire(char *buf)
{
	vms_vector to_target;
	char gun_num;
	short objnum;
	int count = 1;

	memcpy(&to_target, buf+count, 12);      count += 12;
	#ifdef MACINTOSH	// swap the vector to_target
	to_target.x = (fix)INTEL_INT((int)to_target.x);
	to_target.y = (fix)INTEL_INT((int)to_target.y);
	to_target.z = (fix)INTEL_INT((int)to_target.z);
	#endif
	gun_num = buf[count];                                   count += 1;
	objnum = INTEL_SHORT(*(short *)(buf+count));         count += 2;

	Laser_create_new_easy(&to_target, &Gun_pos[gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
}

void
multi_do_create_powerup(char *buf)
{
	short segnum;
	short objnum;
	int my_objnum;
	char pnum;
	int count = 1;
	vms_vector new_pos;
	char powerup_type;

	if (Endlevel_sequence || Control_center_destroyed)
		return;

	pnum = buf[count++];
	powerup_type = buf[count++];
	segnum = INTEL_SHORT(*(short *)(buf+count)); count+=2;
	objnum = INTEL_SHORT(*(short *)(buf+count)); count+=2;

	if ((segnum < 0) || (segnum > Highest_segment_index)) {
		Int3();
		return;
	}
	
	new_pos = *(vms_vector *)(buf+count); count+=sizeof(vms_vector);
#ifdef MACINTOSH
	new_pos.x = (fix)SWAPINT((int)new_pos.x);
	new_pos.y = (fix)SWAPINT((int)new_pos.y);
	new_pos.z = (fix)SWAPINT((int)new_pos.z);
#endif

	Net_create_loc = 0;
	my_objnum = call_object_create_egg(&Objects[Players[pnum].objnum], 1, OBJ_POWERUP, powerup_type);

	if (my_objnum < 0) {
		mprintf((0, "Could not create new powerup!\n"));
		return;
	}

	if (Network_send_objects && network_objnum_is_past(my_objnum))
	{
		mprintf((0, "Resetting object sync due to powerup creation.\n"));
		Network_send_objnum = -1;
	}

	Objects[my_objnum].pos = new_pos;

	vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
	
	obj_relink(my_objnum, segnum);

	map_objnum_local_to_remote(my_objnum, objnum, pnum);

	object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
	mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));

   if (Game_mode & GM_NETWORK)
		PowerupsInMine[powerup_type]++;
}               

void
multi_do_play_sound(char *buf)
{
	int pnum = (int)(buf[1]);
	int sound_num = (int)(buf[2]);
	fix volume = (int)(buf[3]) << 12;

	if (!Players[pnum].connected)
		return;

	Assert(Players[pnum].objnum >= 0);
	Assert(Players[pnum].objnum <= Highest_object_index);

	digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume); 
}

void
multi_do_score(char *buf)
{
	int pnum = (int)(buf[1]);
	
	if ((pnum < 0) || (pnum >= N_players))
	{
		Int3(); // Non-terminal, see rob
		return;
	}

	if (Newdemo_state == ND_STATE_RECORDING)
		newdemo_record_multi_score(pnum, INTEL_INT(*(int *)(buf+2)) );

	Players[pnum].score = INTEL_INT(*(int *)(buf+2));

	multi_sort_kill_list();
}

void
multi_do_trigger(char *buf)
{
	int pnum = (int)(buf[1]);
	int trigger = (int)(buf[2]);
   
   mprintf ((0,"MULTI doing trigger!\n"));

	if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
	{
		Int3(); // Got trigger from illegal playernum
		return;
	}
	if ((trigger < 0) || (trigger >= Num_triggers))
	{
		Int3(); // Illegal trigger number in multiplayer
		return;
	}
	check_trigger_sub(trigger, pnum,0);
}

void multi_do_drop_marker (char *buf)
 {
  int i;
  int pnum=(int)(buf[1]);
  int mesnum=(int)(buf[2]);
  vms_vector position;

  if (pnum==Player_num)  // my marker? don't set it down cuz it might screw up the orientation
   return;

  position.x=(fix)INTEL_INT(*(int *)(buf+3));
  position.y=(fix)INTEL_INT(*(int *)(buf+7));
  position.z=(fix)INTEL_INT(*(int *)(buf+11));

  for (i=0;i<40;i++)
   MarkerMessage[(pnum*2)+mesnum][i]=buf[15+i];

  MarkerPoint[(pnum*2)+mesnum]=position;

  if (MarkerObject[(pnum*2)+mesnum] !=-1 && Objects[MarkerObject[(pnum*2)+mesnum]].type!=OBJ_NONE && MarkerObject[(pnum*2)+mesnum] !=0)
		obj_delete(MarkerObject[(pnum*2)+mesnum]);

  MarkerObject[(pnum*2)+mesnum] = drop_marker_object(&position,Objects[Players[Player_num].objnum].segnum,&Objects[Players[Player_num].objnum].orient,(pnum*2)+mesnum);
  strcpy (MarkerOwner[(pnum*2)+mesnum],Players[pnum].callsign);
  mprintf ((0,"Dropped player %d message: %s\n",pnum,MarkerMessage[(pnum*2)+mesnum]));
 }


void multi_do_hostage_door_status(char *buf)
{
	// Update hit point status of a door

	int count = 1;
	int wallnum; 
	fix hps;

	wallnum = INTEL_SHORT(*(short *)(buf+count));                count += 2;
	hps = (fix)INTEL_INT(*(int *)(buf+count));              count += 4;

	if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
	{
		Int3(); // Non-terminal, see Rob
		return;
	}

//      mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));

	if (hps < Walls[wallnum].hps)
		wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
}

void multi_do_save_game(char *buf)
{
	int count = 1;
	ubyte slot;
	uint id;
	char desc[25];

	slot = *(ubyte *)(buf+count);           count += 1;
	id = INTEL_INT(*(uint *)(buf+count));              count += 4;
	memcpy( desc, &buf[count], 20 );        count += 20;

	multi_save_game( slot, id, desc );
}

void multi_do_restore_game(char *buf)
{
	int count = 1;
	ubyte slot;
	uint id;

	slot = *(ubyte *)(buf+count);           count += 1;
	id = INTEL_INT(*(uint *)(buf+count));              count += 4;

	multi_restore_game( slot, id );
}

 
void multi_do_req_player(char *buf)
{
	netplayer_stats ps;
	ubyte player_n;
	// Send my netplayer_stats to everyone!
	player_n = *(ubyte *)(buf+1);
	if ( (player_n == Player_num) || (player_n == 255)  )   {
		extract_netplayer_stats( &ps, &Players[Player_num] );
		ps.Player_num = Player_num;
		ps.message_type = MULTI_SEND_PLAYER;            // SET
		multi_send_data((ubyte*)&ps, sizeof(netplayer_stats), 0);
	}
}

void multi_do_send_player(char *buf)
{
	// Got a player packet from someone!!!
	netplayer_stats * p;
	p = (netplayer_stats *)buf;     

	Assert( p->Player_num <= N_players );

	mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
	mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));

	use_netplayer_stats( &Players[p->Player_num], p );
}

void
multi_reset_stuff(void)
{
	// A generic, emergency function to solve problems that crop up
	// when a player exits quick-out from the game because of a 
	// serial connection loss.  Fixes several weird bugs!

	dead_player_end();

	Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.

	Dead_player_camera = 0;
	Endlevel_sequence = 0;
	reset_rear_view();
}

void
multi_reset_player_object(object *objp)
{
	int i;

	//Init physics for a non-console player

	Assert(objp >= Objects);
	Assert(objp <= Objects+Highest_object_index);
	Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));

	vm_vec_zero(&objp->mtype.phys_info.velocity);
	vm_vec_zero(&objp->mtype.phys_info.thrust);
	vm_vec_zero(&objp->mtype.phys_info.rotvel);
	vm_vec_zero(&objp->mtype.phys_info.rotthrust);
	objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
	objp->mtype.phys_info.mass = Player_ship->mass;
	objp->mtype.phys_info.drag = Player_ship->drag;
//      objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
	objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);

	//Init render info

	objp->render_type = RT_POLYOBJ;
	objp->rtype.pobj_info.model_num = Player_ship->model_num;               //what model is this?
	objp->rtype.pobj_info.subobj_flags = 0;         //zero the flags
	for (i=0;i<MAX_SUBMODELS;i++)
		vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);

	//reset textures for this, if not player 0

	multi_reset_object_texture (objp);	
	
	// Clear misc

	objp->flags = 0;
	
	if (objp->type == OBJ_GHOST)
		objp->render_type = RT_NONE;

}

void multi_reset_object_texture (object *objp)
 {
	int id,i;

	if (Game_mode & GM_TEAM)
		id = get_team(objp->id);
	else
		id = objp->id;

	if (id == 0)
		objp->rtype.pobj_info.alt_textures=0;
	else {
		Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);

		for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
			multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];

		multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
		multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];

		objp->rtype.pobj_info.alt_textures = id;
   }
 }




extern int TTRecv[];
extern FILE *RecieveLogFile;

void
multi_process_data(char *buf, int len)
{
	// Take an entire message (that has already been checked for validity,
	// if necessary) and act on it.  

	int type;
	len = len;

	type = buf[0];
	
	if (type > MULTI_MAX_TYPE)
	{
		mprintf((1, "multi_process_data: invalid type %d.\n", type));
		Int3();
		return;
	}


   #ifdef NETPROFILING
	   TTRecv[type]++;  
	   fprintf (RecieveLogFile,"Packet type: %d Len:%d TT=%d\n",type,len,TTRecv[type]);
	   fflush (RecieveLogFile);
	#endif
  
	switch(type) 
	{
		case MULTI_POSITION:
			if (!Endlevel_sequence) multi_do_position(buf); break;
		case MULTI_REAPPEAR:
			if (!Endlevel_sequence) multi_do_reappear(buf); break;
		case MULTI_FIRE:
			if (!Endlevel_sequence) multi_do_fire(buf); break;
		case MULTI_KILL:
			multi_do_kill(buf); break;
		case MULTI_REMOVE_OBJECT:
			if (!Endlevel_sequence) multi_do_remobj(buf); break;
		case MULTI_PLAYER_DROP:
		case MULTI_PLAYER_EXPLODE:
			if (!Endlevel_sequence) multi_do_player_explode(buf); break;
		case MULTI_MESSAGE:
			if (!Endlevel_sequence) multi_do_message(buf); break;
		case MULTI_QUIT:
			if (!Endlevel_sequence) multi_do_quit(buf); break;
		case MULTI_BEGIN_SYNC:
		   break;
		case MULTI_CONTROLCEN:
			if (!Endlevel_sequence) multi_do_controlcen_destroy(buf); break;
		case MULTI_POWERUP_UPDATE:
			if (!Endlevel_sequence) multi_do_powerup_update(buf); break;
		case MULTI_SOUND_FUNCTION:
			multi_do_sound_function(buf); break;
      case MULTI_MARKER:
	 if (!Endlevel_sequence) multi_do_drop_marker (buf); break;
      case MULTI_DROP_WEAPON:
	 if (!Endlevel_sequence) multi_do_drop_weapon(buf); break;
      case MULTI_DROP_FLAG:
	 if (!Endlevel_sequence) multi_do_drop_flag(buf); break;
      case MULTI_GUIDED:
	 if (!Endlevel_sequence) multi_do_guided (buf); break;
      case MULTI_STOLEN_ITEMS:
	 if (!Endlevel_sequence) multi_do_stolen_items(buf); break;
      case MULTI_WALL_STATUS:
	 if (!Endlevel_sequence) multi_do_wall_status(buf); break;
      case MULTI_HEARTBEAT:
	 if (!Endlevel_sequence) multi_do_heartbeat (buf); break;
      case MULTI_SEISMIC:
		if (!Endlevel_sequence) multi_do_seismic (buf); break;
	   case MULTI_LIGHT:
		if (!Endlevel_sequence) multi_do_light (buf); break;
      case MULTI_KILLGOALS:
 		
		 if (!Endlevel_sequence) multi_do_kill_goal_counts (buf); break;
		case MULTI_ENDLEVEL_START:
			if (!Endlevel_sequence) multi_do_escape(buf); break;
		case MULTI_END_SYNC:
			break;
		case MULTI_CLOAK:
			if (!Endlevel_sequence) multi_do_cloak(buf); break;
		case MULTI_DECLOAK:
			if (!Endlevel_sequence) multi_do_decloak(buf); break;
		case MULTI_DOOR_OPEN:
			if (!Endlevel_sequence) multi_do_door_open(buf); break;
		case MULTI_CREATE_EXPLOSION:
			if (!Endlevel_sequence) multi_do_create_explosion(buf); break;
		case MULTI_CONTROLCEN_FIRE:
			if (!Endlevel_sequence) multi_do_controlcen_fire(buf); break;
		case MULTI_CREATE_POWERUP:
			if (!Endlevel_sequence) multi_do_create_powerup(buf); break;
		case MULTI_PLAY_SOUND:
			if (!Endlevel_sequence) multi_do_play_sound(buf); break;
		case MULTI_CAPTURE_BONUS:
			if (!Endlevel_sequence) multi_do_capture_bonus(buf); break;
		case MULTI_ORB_BONUS:
			if (!Endlevel_sequence) multi_do_orb_bonus(buf); break;
		case MULTI_GOT_FLAG:
			if (!Endlevel_sequence) multi_do_got_flag(buf); break;
		case MULTI_GOT_ORB:
			if (!Endlevel_sequence) multi_do_got_orb(buf); break;
		case MULTI_PLAY_BY_PLAY:
			if (!Endlevel_sequence) multi_do_play_by_play(buf); break;
		case MULTI_RANK:
			if (!Endlevel_sequence) multi_do_ranking (buf); break;
		case MULTI_MODEM_PING:
			if (!Endlevel_sequence) multi_send_modem_ping_return(); break;
		case MULTI_MODEM_PING_RETURN:
			if (!Endlevel_sequence) multi_do_modem_ping_return(); break;
#ifndef SHAREWARE
		case MULTI_FINISH_GAME:
			multi_do_finish_game(buf);	break;  // do this one regardless of endsequence	
		case MULTI_ROBOT_CONTROLS:
			if (!Endlevel_sequence) multi_do_robot_controls(buf); break;
		case MULTI_ROBOT_CLAIM:
			if (!Endlevel_sequence) multi_do_claim_robot(buf); break;
		case MULTI_ROBOT_POSITION:
			if (!Endlevel_sequence) multi_do_robot_position(buf); break;
		case MULTI_ROBOT_EXPLODE:
			if (!Endlevel_sequence) multi_do_robot_explode(buf); break;
		case MULTI_ROBOT_RELEASE:
			if (!Endlevel_sequence) multi_do_release_robot(buf); break;
		case MULTI_ROBOT_FIRE:
			if (!Endlevel_sequence) multi_do_robot_fire(buf); break;
#endif
		case MULTI_SCORE:
			if (!Endlevel_sequence) multi_do_score(buf); break;
		case MULTI_CREATE_ROBOT:
			if (!Endlevel_sequence) multi_do_create_robot(buf); break;
		case MULTI_TRIGGER:
			if (!Endlevel_sequence) multi_do_trigger(buf); break;
		case MULTI_START_TRIGGER:
			if (!Endlevel_sequence) multi_do_start_trigger(buf); break;
		case MULTI_FLAGS:
			if (!Endlevel_sequence) multi_do_flags(buf); break;
		case MULTI_DROP_BLOB:
			if (!Endlevel_sequence) multi_do_drop_blob(buf); break;
	   case MULTI_ACTIVE_DOOR:
			if (!Endlevel_sequence) multi_do_active_door(buf); break;
		case MULTI_BOSS_ACTIONS:
			if (!Endlevel_sequence) multi_do_boss_actions(buf); break;
		case MULTI_CREATE_ROBOT_POWERUPS:
			if (!Endlevel_sequence) multi_do_create_robot_powerups(buf); break;
		case MULTI_HOSTAGE_DOOR:
			if (!Endlevel_sequence) multi_do_hostage_door_status(buf); break;
		case MULTI_SAVE_GAME:
			if (!Endlevel_sequence) multi_do_save_game(buf); break;
		case MULTI_RESTORE_GAME:
			if (!Endlevel_sequence) multi_do_restore_game(buf); break;
		case MULTI_REQ_PLAYER:
			if (!Endlevel_sequence) multi_do_req_player(buf); break;
		case MULTI_SEND_PLAYER:
			if (!Endlevel_sequence) multi_do_send_player(buf); break;

		default:
			mprintf((1, "Invalid type in multi_process_input().\n"));
			Int3();
	}
}

void
multi_process_bigdata(char *buf, int len)
{
	// Takes a bunch of messages, check them for validity,
	// and pass them to multi_process_data. 

	int type, sub_len, bytes_processed = 0;

	while( bytes_processed < len )  {
		type = buf[bytes_processed];

		if ( (type<0) || (type>MULTI_MAX_TYPE)) {
			mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
			return;
		}
		sub_len = message_length[type];

		Assert(sub_len > 0);

		if ( (bytes_processed+sub_len) > len )  {
			mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
			Int3();
			return;
		}

		multi_process_data(&buf[bytes_processed], sub_len);
		bytes_processed += sub_len;
	}
}

//
// Part 2 : Functions that send communication messages to inform the other
//          players of something we did.
//

void
multi_send_fire(void)
{
	if (!Network_laser_fired)
		return;

	multibuf[0] = (char)MULTI_FIRE;
	multibuf[1] = (char)Player_num;
	multibuf[2] = (char)Network_laser_gun;
	multibuf[3] = (char)Network_laser_level;
	multibuf[4] = (char)Network_laser_flags;
	multibuf[5] = (char)Network_laser_fired;
       
	*(short *)(multibuf+6) = INTEL_SHORT(Network_laser_track);

	multi_send_data(multibuf, 8, 0);
	
	Network_laser_fired = 0;
}

void
multi_send_destroy_controlcen(int objnum, int player)
{
	if (player == Player_num)
		HUD_init_message(TXT_YOU_DEST_CONTROL);
	else if ((player > 0) && (player < N_players))
		HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
	else
		HUD_init_message(TXT_CONTROL_DESTROYED);

	multibuf[0] = (char)MULTI_CONTROLCEN;
	*(ushort *)(multibuf+1) = INTEL_SHORT(objnum);
	multibuf[3] = player;
   multi_send_data(multibuf, 4, 2);
}

void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[])
 {
  int i;

   if (player<N_players)
     {
       mprintf ((0,"Sending MARKER drop!\n"));
       multibuf[0]=(char)MULTI_MARKER;
       multibuf[1]=(char)player;
       multibuf[2]=messagenum;
       *(fix *)(multibuf+3)=INTEL_INT(position.x);
       *(fix *)(multibuf+7)=INTEL_INT(position.y);
       *(fix *)(multibuf+11)=INTEL_INT(position.z);
       for (i=0;i<40;i++)
	     multibuf[15+i]=text[i];
     }
   multi_send_data(multibuf, 55, 1);
 }
			   
void 
multi_send_endlevel_start(int secret)
{
	multibuf[0] = (char)MULTI_ENDLEVEL_START;
	multibuf[1] = Player_num;
	multibuf[2] = (char)secret;
	
	if ((secret) && !multi_goto_secret)
		multi_goto_secret = 1;
	else if (!multi_goto_secret)
		multi_goto_secret = 2;

	multi_send_data(multibuf, 3, 1);
	if (Game_mode & GM_NETWORK)
	{
		Players[Player_num].connected = 5;
		network_send_endlevel_packet();
	}
}

void
multi_send_player_explode(char type)
{
	int count = 0;
	int i;

	Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );

	multi_send_position(Players[Player_num].objnum);

	if (Network_send_objects)
	{
		mprintf((0, "Resetting object sync due to player explosion.\n"));
		Network_send_objnum = -1;
	}
	
	multibuf[count++] = type;
	multibuf[count++] = Player_num;
  
	*(ushort *)(multibuf+count) = INTEL_SHORT((ushort)Players[Player_num].primary_weapon_flags);
	count += 2;
	*(ushort *)(multibuf+count) = INTEL_SHORT((ushort)Players[Player_num].secondary_weapon_flags);
	count += 2;
	multibuf[count++] = (char)Players[Player_num].laser_level;
	
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];

	multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE1_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[GUIDED_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_MINE_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE4_INDEX];
	multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE5_INDEX];

	*(ushort *)(multibuf+count) = INTEL_SHORT( (ushort)Players[Player_num].primary_ammo[VULCAN_INDEX] );
	count += 2;
	*(ushort *)(multibuf+count) = INTEL_SHORT( (ushort)Players[Player_num].primary_ammo[GAUSS_INDEX] );
	count += 2;
	*(uint *)(multibuf+count) = INTEL_INT( (uint)Players[Player_num].flags );
	count += 4;
	
	multibuf[count++] = Net_create_loc;

	Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);

	memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
	
	mprintf((0, "Created %d explosion objects.\n", Net_create_loc));

	for (i = 0; i < Net_create_loc; i++)
	{
		if (Net_create_objnums[i] <= 0) {
			Int3(); // Illegal value in created egg object numbers
			count +=2;
			continue;
		}

		*(short *)(multibuf+count) = INTEL_SHORT( (short)Net_create_objnums[i] ); count += 2;

		// We created these objs so our local number = the network number
		map_objnum_local_to_local((short)Net_create_objnums[i]);
	}

	Net_create_loc = 0;

//      mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));

	if (count > message_length[MULTI_PLAYER_EXPLODE])
	{
		Int3(); // See Rob
	}

	multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
	if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
		multi_send_decloak();
	if (Game_mode & GM_MULTI_ROBOTS)
		multi_strip_robots(Player_num);
}

extern ubyte Secondary_weapon_to_powerup[];
extern ubyte Primary_weapon_to_powerup[];

// put a lid on how many objects will be spewed by an exploding player
// to prevent rampant powerups in netgames

void multi_cap_objects ()
 {
  char type,flagtype;
  int index;

  if (!(Game_mode & GM_NETWORK))
  	return;

  for (index=0;index<MAX_PRIMARY_WEAPONS;index++)	
	{
		type=Primary_weapon_to_powerup[index];
   	if (PowerupsInMine[type]>=MaxPowerupsAllowed[type])
 		 if(Players[Player_num].primary_weapon_flags & (1 << index))
			{
			 mprintf ((0,"PIM=%d MPA=%d\n",PowerupsInMine[type],MaxPowerupsAllowed[type]));
			 mprintf ((0,"Killing a primary cuz there's too many! (%d)\n",type));
 		    Players[Player_num].primary_weapon_flags&=(~(1 << index));
			}
	}
  

  // Don't do the adjustment stuff for Hoard mode
  if (!(Game_mode & GM_HOARD))	  	  
	  Players[Player_num].secondary_ammo[2]/=4;

  Players[Player_num].secondary_ammo[7]/=4;
	
  for (index=0;index<MAX_SECONDARY_WEAPONS;index++)	
	{
		if ((Game_mode & GM_HOARD) && index==PROXIMITY_INDEX)
			continue;

		type=Secondary_weapon_to_powerup[index];
		
   	if ((Players[Player_num].secondary_ammo[index]+PowerupsInMine[type])>MaxPowerupsAllowed[type])
		 {
        if (MaxPowerupsAllowed[type]-PowerupsInMine[type]<0)
			 Players[Player_num].secondary_ammo[index]=0;
		 else	
	    	 Players[Player_num].secondary_ammo[index]=(MaxPowerupsAllowed[type]-PowerupsInMine[type]);
		  
			mprintf ((0,"Hey! I killed secondary type %d because PIM=%d MPA=%d\n",type,PowerupsInMine[type],MaxPowerupsAllowed[type]));			
		 }	
	}

  if (!(Game_mode & GM_HOARD))	  	  
	  Players[Player_num].secondary_ammo[2]*=4;
  Players[Player_num].secondary_ammo[7]*=4;
    
		if (Players[Player_num].laser_level > MAX_LASER_LEVEL)
		 if (PowerupsInMine[POW_SUPER_LASER]+1 > MaxPowerupsAllowed[POW_SUPER_LASER])
			Players[Player_num].laser_level=0;

		if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
		 if (PowerupsInMine[POW_QUAD_FIRE]+1 > MaxPowerupsAllowed[POW_QUAD_FIRE])
			Players[Player_num].flags&=(~PLAYER_FLAGS_QUAD_LASERS);

		if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
		 if (PowerupsInMine[POW_CLOAK]+1 > MaxPowerupsAllowed[POW_CLOAK])
			Players[Player_num].flags&=(~PLAYER_FLAGS_CLOAKED);

		if (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL)
		 if (PowerupsInMine[POW_FULL_MAP]+1 > MaxPowerupsAllowed[POW_FULL_MAP])
			Players[Player_num].flags&=(~PLAYER_FLAGS_MAP_ALL);

		if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
		 if (PowerupsInMine[POW_AFTERBURNER]+1 > MaxPowerupsAllowed[POW_AFTERBURNER])
			Players[Player_num].flags&=(~PLAYER_FLAGS_AFTERBURNER);

		if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
		 if (PowerupsInMine[POW_AMMO_RACK]+1 > MaxPowerupsAllowed[POW_AMMO_RACK])
			Players[Player_num].flags&=(~PLAYER_FLAGS_AMMO_RACK);

		if (Players[Player_num].flags & PLAYER_FLAGS_CONVERTER)
		 if (PowerupsInMine[POW_CONVERTER]+1 > MaxPowerupsAllowed[POW_CONVERTER])
			Players[Player_num].flags&=(~PLAYER_FLAGS_CONVERTER);

		if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT)
		 if (PowerupsInMine[POW_HEADLIGHT]+1 > MaxPowerupsAllowed[POW_HEADLIGHT])
			Players[Player_num].flags&=(~PLAYER_FLAGS_HEADLIGHT);
		
		if (Game_mode & GM_CAPTURE)
		{
			if (Players[Player_num].flags & PLAYER_FLAGS_FLAG)
			 {
			  if (get_team(Player_num)==TEAM_RED)
				flagtype=POW_FLAG_BLUE;
			  else
				flagtype=POW_FLAG_RED;
			
			  if (PowerupsInMine[flagtype]+1 > MaxPowerupsAllowed[flagtype])
			     Players[Player_num].flags&=(~PLAYER_FLAGS_FLAG);
			 }
		}

 }

// adds players inventory to multi cap

void multi_adjust_cap_for_player (int pnum)
 {
  char type;
  
  int index;

  if (!(Game_mode & GM_NETWORK))
  	return;

  for (index=0;index<MAX_PRIMARY_WEAPONS;index++)	
	{
		type=Primary_weapon_to_powerup[index];
	     if (Players[pnum].primary_weapon_flags & (1 << index))
 		    MaxPowerupsAllowed[type]++;
	}
			
  for (index=0;index<MAX_SECONDARY_WEAPONS;index++)	
	{
		type=Secondary_weapon_to_powerup[index];
  		MaxPowerupsAllowed[type]+=Players[pnum].secondary_ammo[index];
	}

  if (Players[pnum].laser_level > MAX_LASER_LEVEL)
  	 MaxPowerupsAllowed[POW_SUPER_LASER]++;

  if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
    MaxPowerupsAllowed[POW_QUAD_FIRE]++;

  if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
	 MaxPowerupsAllowed[POW_CLOAK]++;

  if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
	 MaxPowerupsAllowed[POW_FULL_MAP]++;

  if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
	MaxPowerupsAllowed[POW_AFTERBURNER]++;

  if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
	  MaxPowerupsAllowed[POW_AMMO_RACK]++;

  if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
	 MaxPowerupsAllowed[POW_CONVERTER]++;

  if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
	 MaxPowerupsAllowed[POW_HEADLIGHT]++;
 }

void multi_adjust_remote_cap (int pnum)
 {
  char type;
  
  int index;

  if (!(Game_mode & GM_NETWORK))
  	return;

  for (index=0;index<MAX_PRIMARY_WEAPONS;index++)	
	{
		type=Primary_weapon_to_powerup[index];
	     if (Players[pnum].primary_weapon_flags & (1 << index))
 		    PowerupsInMine[type]++;
	}
			
  for (index=0;index<MAX_SECONDARY_WEAPONS;index++)	
	{
		type=Secondary_weapon_to_powerup[index];

		if ((Game_mode & GM_HOARD) && index==2)
			continue;
   
      if (index==2 || index==7) // PROX or SMARTMINES? Those bastards...
 		 	PowerupsInMine[type]+=(Players[pnum].secondary_ammo[index]/4);
		else
 	 	   PowerupsInMine[type]+=Players[pnum].secondary_ammo[index];
 
	}

  if (Players[pnum].laser_level > MAX_LASER_LEVEL)
  	 PowerupsInMine[POW_SUPER_LASER]++;

  if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
    PowerupsInMine[POW_QUAD_FIRE]++;

  if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
	 PowerupsInMine[POW_CLOAK]++;

  if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
	 PowerupsInMine[POW_FULL_MAP]++;

  if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
	PowerupsInMine[POW_AFTERBURNER]++;

  if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
	  PowerupsInMine[POW_AMMO_RACK]++;

  if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
	 PowerupsInMine[POW_CONVERTER]++;

  if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
	 PowerupsInMine[POW_HEADLIGHT]++;

 }

void
multi_send_message(void)
{
	int loc = 0;
	if (Network_message_reciever != -1)
	{
		multibuf[loc] = (char)MULTI_MESSAGE;            loc += 1;
		multibuf[loc] = (char)Player_num;                       loc += 1;
		strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
		multibuf[loc-1] = '\0';
		multi_send_data(multibuf, loc, 0);
		Network_message_reciever = -1;
	}
}

void
multi_send_reappear()
{
	multibuf[0] = (char)MULTI_REAPPEAR;
	*(short *)(multibuf+1) = INTEL_SHORT(Players[Player_num].objnum);
	   
	multi_send_data(multibuf, 3, 2);
	PKilledFlags[Player_num]=0;
}

void
multi_send_position(int objnum)
{
#ifdef MACINTOSH
	shortpos sp;
#endif
	int count=0;

	if (Game_mode & GM_NETWORK) {
		return;
	}
		     
	multibuf[count++] = (char)MULTI_POSITION;
#ifndef MACINTOSH
	create_shortpos((shortpos *)(multibuf+count), Objects+objnum,0);
	count += sizeof(shortpos);
#else
	create_shortpos(&sp, Objects+objnum, 1);
	memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
	count += 9;
	memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
	count += 14;
#endif

	multi_send_data(multibuf, count, 0);
}

void
multi_send_kill(int objnum)
{
	// I died, tell the world.

	int killer_objnum;
	int count = 0;

	Assert(Objects[objnum].id == Player_num);
	killer_objnum = Players[Player_num].killer_objnum;

	multi_compute_kill(killer_objnum, objnum);
	 
	multibuf[0] = (char)MULTI_KILL;     count += 1;
	multibuf[1] = Player_num;           count += 1;
   if (killer_objnum > -1) {
		short s;		// do it with variable since INTEL_SHORT won't work on return val from function.
		
		s = (short)objnum_local_to_remote(killer_objnum, (byte *)&multibuf[count+2]);
		*(short *)(multibuf+count) = INTEL_SHORT(s);
	} 
	else
	{
		*(short *)(multibuf+count) = INTEL_SHORT((short)-1);
		multibuf[count+2] = (char)-1;
	}
	count += 3;
	multi_send_data(multibuf, count, 1);

#ifndef SHAREWARE
	if (Game_mode & GM_MULTI_ROBOTS)
		multi_strip_robots(Player_num);
#endif
}

void
multi_send_remobj(int objnum)
{
	// Tell the other guy to remove an object from his list

	byte obj_owner;
	short remote_objnum;

   if (Objects[objnum].type==OBJ_POWERUP && (Game_mode & GM_NETWORK))
    {
		if (PowerupsInMine[Objects[objnum].id] > 0)
		 {
	      PowerupsInMine[Objects[objnum].id]--;
		  if (multi_powerup_is_4pack (Objects[objnum].id))
			 {
				mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
		
				if (PowerupsInMine[Objects[objnum].id-1]-4<0)
					PowerupsInMine[Objects[objnum].id-1]=0;
				else
					PowerupsInMine[Objects[objnum].id-1]-=4;
			 }
		 }
		   	
	 }
			
	multibuf[0] = (char)MULTI_REMOVE_OBJECT;

	remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);

	*(short *)(multibuf+1) = INTEL_SHORT(remote_objnum); // Map to network objnums

	multibuf[3] = obj_owner;        

//      mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));

	multi_send_data(multibuf, 4, 0);

	if (Network_send_objects && network_objnum_is_past(objnum))
	{
		mprintf((0, "Resetting object sync due to object removal.\n"));
		Network_send_objnum = -1;
	}
}
	
void
multi_send_quit(int why)
{
	// I am quitting the game, tell the other guy the bad news.

	Assert (why == MULTI_QUIT);

	multibuf[0] = (char)why;
	multibuf[1] = Player_num;
	multi_send_data(multibuf, 2, 1);

}

void
multi_send_cloak(void)
{
	// Broadcast a change in our pflags (made to support cloaking)

	multibuf[0] = MULTI_CLOAK;
	multibuf[1] = (char)Player_num;

	multi_send_data(multibuf, 2, 1);

#ifndef SHAREWARE
	if (Game_mode & GM_MULTI_ROBOTS)
		multi_strip_robots(Player_num);
#endif
}

void
multi_send_decloak(void)
{
	// Broadcast a change in our pflags (made to support cloaking)

	multibuf[0] = MULTI_DECLOAK;
	multibuf[1] = (char)Player_num;

	multi_send_data(multibuf, 2, 1);
}

void
multi_send_door_open(int segnum, int side,ubyte flag)
{
	// When we open a door make sure everyone else opens that door

	multibuf[0] = MULTI_DOOR_OPEN;
	*(short *)(multibuf+1) = INTEL_SHORT( (short)segnum );
	multibuf[3] = (byte)side;
   multibuf[4] = flag;
   
	multi_send_data(multibuf, 5, 2);
}

void
multi_send_door_open_specific(int pnum,int segnum, int side,ubyte flag)
{
	// For sending doors only to a specific person (usually when they're joining)

	Assert (Game_mode & GM_NETWORK);
//   Assert (pnum>-1 && pnum<N_players);
 
	multibuf[0] = MULTI_DOOR_OPEN;
	*(short *)(multibuf+1) = INTEL_SHORT( (short)segnum );
	multibuf[3] = (byte)side;
   multibuf[4] = flag;
   
	network_send_naked_packet(multibuf, 5, pnum);
}

//
// Part 3 : Functions that change or prepare the game for multiplayer use.
//          Not including functions needed to syncronize or start the 
//          particular type of multiplayer game.  Includes preparing the
//                      mines, player structures, etc.

void
multi_send_create_explosion(int pnum)
{
	// Send all data needed to create a remote explosion

	int count = 0;

	multibuf[count] = MULTI_CREATE_EXPLOSION;       count += 1;
	multibuf[count] = (byte)pnum;                                   count += 1;
	//                                                                                                      -----------
	//                                                                                                      Total size = 2

	multi_send_data(multibuf, count, 0);
}
	
void
multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
{
#ifdef MACINTOSH
	vms_vector swapped_vec;
#endif
	int count = 0;

	multibuf[count] = MULTI_CONTROLCEN_FIRE;                count +=  1;
	#ifndef MACINTOSH
	memcpy(multibuf+count, to_goal, 12);                    count += 12;
	#else
	swapped_vec.x = (fix)INTEL_INT( (int)to_goal->x );
	swapped_vec.y = (fix)INTEL_INT( (int)to_goal->y );
	swapped_vec.z = (fix)INTEL_INT( (int)to_goal->z );
	memcpy(multibuf+count, &swapped_vec, 12);				count += 12;
	#endif
	multibuf[count] = (char)best_gun_num;                   count +=  1;
	*(short *)(multibuf+count) = INTEL_SHORT( (short)objnum );     count +=  2;
	//                                                                                                                      ------------
	//                                                                                                                      Total  = 16
	multi_send_data(multibuf, count, 0);
}

void
multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
{
	// Create a powerup on a remote machine, used for remote
	// placement of used powerups like missiles and cloaking
	// powerups.

#ifdef MACINTOSH
	vms_vector swapped_vec;
#endif
	int count = 0;

   if (Game_mode & GM_NETWORK)
	   PowerupsInMine[powerup_type]++;

	multibuf[count] = MULTI_CREATE_POWERUP;         count += 1;
	multibuf[count] = Player_num;                                      count += 1;
	multibuf[count] = powerup_type;                                 count += 1;
	*(short *)(multibuf+count) = INTEL_SHORT( (short)segnum );     count += 2;
	*(short *)(multibuf+count) = INTEL_SHORT( (short)objnum );     count += 2;
#ifndef MACINTOSH
	*(vms_vector *)(multibuf+count) = *pos;         count += sizeof(vms_vector);
#else
	swapped_vec.x = (fix)INTEL_INT( (int)pos->x );
	swapped_vec.y = (fix)INTEL_INT( (int)pos->y );
	swapped_vec.z = (fix)INTEL_INT( (int)pos->z );
	memcpy(multibuf+count, &swapped_vec, 12);				count += 12;
#endif
	//                                                                                                            -----------
	//                                                                                                            Total =  19
	multi_send_data(multibuf, count, 2);

	if (Network_send_objects && network_objnum_is_past(objnum))
	{
		mprintf((0, "Resetting object sync due to powerup creation.\n"));
		Network_send_objnum = -1;
	}

	mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
	map_objnum_local_to_local(objnum);
}       

void
multi_send_play_sound(int sound_num, fix volume)
{
	int count = 0;
	multibuf[count] = MULTI_PLAY_SOUND;                     count += 1;
	multibuf[count] = Player_num;                                   count += 1;
	multibuf[count] = (char)sound_num;                      count += 1;
	multibuf[count] = (char)(volume >> 12); count += 1;
	//                                                                                                         -----------
	//                                                                                                         Total = 4
	multi_send_data(multibuf, count, 0);
}

#pragma off (unreferenced)
void
multi_send_audio_taunt(int taunt_num)
{
	return; // Taken out, awaiting sounds..

#if 0
	int audio_taunts[4] = {
		SOUND_CONTROL_CENTER_WARNING_SIREN,
		SOUND_HOSTAGE_RESCUED,
		SOUND_REFUEL_STATION_GIVING_FUEL,
		SOUND_BAD_SELECTION
	};


	Assert(taunt_num >= 0);
	Assert(taunt_num < 4);

	digi_play_sample( audio_taunts[taunt_num], F1_0 );
	multi_send_play_sound(audio_taunts[taunt_num], F1_0);
#endif
}
#pragma on (unreferenced)

void
multi_send_score(void)
{
	// Send my current score to all other players so it will remain
	// synced.
	int count = 0;

	if (Game_mode & GM_MULTI_COOP) {
		multi_sort_kill_list();
		multibuf[count] = MULTI_SCORE;                  count += 1;
		multibuf[count] = Player_num;                           count += 1;
		*(int *)(multibuf+count) = INTEL_INT( Players[Player_num].score );  count += 4;
		multi_send_data(multibuf, count, 0);
	}
}       


void
multi_send_save_game(ubyte slot, uint id, char * desc)
{
	int count = 0;
	
	multibuf[count] = MULTI_SAVE_GAME;              count += 1;
	multibuf[count] = slot;                         count += 1;    // Save slot=0
	*(uint *)(multibuf+count) = INTEL_INT( id );         count += 4;             // Save id
	memcpy( &multibuf[count], desc, 20 ); count += 20;

	multi_send_data(multibuf, count, 2);
}

void
multi_send_restore_game(ubyte slot, uint id)
{
	int count = 0;
	
	multibuf[count] = MULTI_RESTORE_GAME;   count += 1;
	multibuf[count] = slot;                                                 count += 1;             // Save slot=0
	*(uint *)(multibuf+count) = INTEL_INT( id );         count += 4;             // Save id

	multi_send_data(multibuf, count, 2);
}

void
multi_send_netplayer_stats_request(ubyte player_num)
{
	int count = 0;
	
	multibuf[count] = MULTI_REQ_PLAYER;     count += 1;
	multibuf[count] = player_num;                   count += 1;

	multi_send_data(multibuf, count, 0 );
}

void
multi_send_trigger(int triggernum)
{
	// Send an even to trigger something in the mine
	
	int count = 0;
	
	multibuf[count] = MULTI_TRIGGER;                                count += 1;
	multibuf[count] = Player_num;                                   count += 1;
	multibuf[count] = (ubyte)triggernum;            count += 1;
   
   mprintf ((0,"Sending trigger %d\n",triggernum));

	multi_send_data(multibuf, count, 1); 
//	multi_send_data(multibuf, count, 1); // twice? 
}

void
multi_send_hostage_door_status(int wallnum)
{
	// Tell the other player what the hit point status of a hostage door
	// should be

	int count = 0;
	
	Assert(Walls[wallnum].type == WALL_BLASTABLE);

	multibuf[count] = MULTI_HOSTAGE_DOOR;           count += 1;
	*(short *)(multibuf+count) = INTEL_SHORT( (short)wallnum );           count += 2;
	*(fix *)(multibuf+count) = (fix)INTEL_INT( (int)Walls[wallnum].hps );  count += 4;

//      mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));

	multi_send_data(multibuf, count, 0);
}

extern int ConsistencyCount;
extern int Drop_afterburner_blob_flag;
int PhallicLimit=0;
int PhallicMan=-1;

void multi_prep_level(void)
{
	// Do any special stuff to the level required for serial games
	// before we begin playing in it.

	// Player_num MUST be set before calling this procedure.  

	// This function must be called before checksuming the Object array,
	// since the resulting checksum with depend on the value of Player_num
	// at the time this is called.

	int i,ng=0;
	int     cloak_count, inv_count;

	Assert(Game_mode & GM_MULTI);

	Assert(NumNetPlayerPositions > 0);
	
   PhallicLimit=0;
   PhallicMan=-1;
   Drop_afterburner_blob_flag=0;
   ConsistencyCount=0;
  
   for (i=0;i<MAX_NUM_NET_PLAYERS;i++)
	 PKilledFlags[i]=0;
  
	for (i = 0; i < NumNetPlayerPositions; i++)
	{
		if (i != Player_num)
			Objects[Players[i].objnum].control_type = CT_REMOTE;
		Objects[Players[i].objnum].movement_type = MT_PHYSICS;
		multi_reset_player_object(&Objects[Players[i].objnum]);
		LastPacketTime[i] = 0;
	}

#ifndef SHAREWARE
	for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
	{
		robot_controlled[i] = -1;       
		robot_agitation[i] = 0;
		robot_fired[i] = 0;
	}
#endif

	Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];

	if (!(Game_mode & GM_MULTI_COOP))       
	{
		multi_delete_extra_objects(); // Removes monsters from level
	}

	if (Game_mode & GM_MULTI_ROBOTS)
	{
		multi_set_robot_ai(); // Set all Robot AI to types we can cope with
	}
 
   if (Game_mode & GM_NETWORK)
	 {
	  multi_adjust_cap_for_player(Player_num);	
	  multi_send_powerup_update();
	  ng=1;  // ng means network game
	 }
   ng=1;	

	inv_count = 0;
	cloak_count = 0;
	for (i=0; i<=Highest_object_index; i++)
	{
		int objnum;

		if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
		{
			objnum = obj_create(OBJ_POWERUP, POW_SHIELD_BOOST, Objects[i].segnum, &Objects[i].pos, &vmd_identity_matrix, Powerup_info[POW_SHIELD_BOOST].size, CT_POWERUP, MT_PHYSICS, RT_POWERUP);
			obj_delete(i);                  
			if (objnum != -1)
			{
				Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
				Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
				Objects[objnum].rtype.vclip_info.framenum = 0;
				Objects[objnum].mtype.phys_info.drag = 512;     //1024;
				Objects[objnum].mtype.phys_info.mass = F1_0;
				vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
			}
			continue;
		}

		if (Objects[i].type == OBJ_POWERUP)
		{
			if (Objects[i].id == POW_EXTRA_LIFE) 
			{
		   if (ng && !Netgame.DoInvulnerability)
		      {
				 Objects[i].id = POW_SHIELD_BOOST;
				 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
				 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
		      }
		   else
		      {
				 Objects[i].id = POW_INVULNERABILITY;
				 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
				 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
		      } 

	      }

			if (!(Game_mode & GM_MULTI_COOP))
				if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
					{
						Objects[i].id = POW_SHIELD_BOOST;
						Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
						Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
					}

			if (Objects[i].id == POW_INVULNERABILITY) {
			   if (inv_count >= 3 || (ng && !Netgame.DoInvulnerability)) {
					mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
					Objects[i].id = POW_SHIELD_BOOST;
					Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
					Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
				} else
					inv_count++;
			}

			if (Objects[i].id == POW_CLOAK) {
	   if (cloak_count >= 3 || (ng && !Netgame.DoCloak)) {
					mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
					Objects[i].id = POW_SHIELD_BOOST;
					Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
					Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
				} else
					cloak_count++;
			}

	if (Objects[i].id == POW_AFTERBURNER && ng && !Netgame.DoAfterburner)
	   bash_to_shield (i,"afterburner");
	if (Objects[i].id == POW_FUSION_WEAPON && ng &&  !Netgame.DoFusions)
	   bash_to_shield (i,"fusion");
	if (Objects[i].id == POW_PHOENIX_WEAPON && ng && !Netgame.DoPhoenix)
	   bash_to_shield (i,"phoenix");

	if (Objects[i].id == POW_HELIX_WEAPON && ng && !Netgame.DoHelix)
	   bash_to_shield (i,"helix");

	if (Objects[i].id == POW_MEGA_WEAPON && ng && !Netgame.DoMegas)
	   bash_to_shield (i,"mega");

	if (Objects[i].id == POW_SMARTBOMB_WEAPON && ng && !Netgame.DoSmarts)
	   bash_to_shield (i,"smartmissile");

	if (Objects[i].id == POW_GAUSS_WEAPON && ng && !Netgame.DoGauss)
	   bash_to_shield (i,"gauss");

	if (Objects[i].id == POW_VULCAN_WEAPON && ng && !Netgame.DoVulcan)
	   bash_to_shield (i,"vulcan");
			
	if (Objects[i].id == POW_PLASMA_WEAPON && ng && !Netgame.DoPlasma)
	   bash_to_shield (i,"plasma");
		 
	if (Objects[i].id == POW_OMEGA_WEAPON && ng && !Netgame.DoOmega)
	   bash_to_shield (i,"omega");

	if (Objects[i].id == POW_SUPER_LASER && ng && !Netgame.DoSuperLaser)
	   bash_to_shield (i,"superlaser");

	if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && !Netgame.DoProximity)
	   bash_to_shield (i,"proximity");

//    Special: Make all proximity bombs into shields if in hoard mode because
//		we use the proximity slot in the player struct to signify how many orbs
//		the player has.

	if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && (Game_mode & GM_HOARD))
	   bash_to_shield (i,"proximity");

	if (Objects[i].id==POW_VULCAN_AMMO && ng && (!Netgame.DoVulcan && !Netgame.DoGauss))
		bash_to_shield(i,"vulcan ammo");

	if (Objects[i].id == POW_SPREADFIRE_WEAPON && ng && !Netgame.DoSpread)
	   bash_to_shield (i,"spread");
	if (Objects[i].id == POW_SMART_MINE && ng && !Netgame.DoSmartMine)
	   bash_to_shield (i,"smartmine");
	if (Objects[i].id == POW_SMISSILE1_1 && ng &&  !Netgame.DoFlash)
	   bash_to_shield (i,"flash");
	if (Objects[i].id == POW_SMISSILE1_4 && ng &&  !Netgame.DoFlash)
	   bash_to_shield (i,"flash");
	if (Objects[i].id == POW_GUIDED_MISSILE_1 && ng &&  !Netgame.DoGuided)
	   bash_to_shield (i,"guided");
	if (Objects[i].id == POW_GUIDED_MISSILE_4 && ng &&  !Netgame.DoGuided)
	   bash_to_shield (i,"guided");
	if (Objects[i].id == POW_EARTHSHAKER_MISSILE && ng &&  !Netgame.DoEarthShaker)
	   bash_to_shield (i,"earth");
	if (Objects[i].id == POW_MERCURY_MISSILE_1 && ng &&  !Netgame.DoMercury)
	   bash_to_shield (i,"Mercury");
	if (Objects[i].id == POW_MERCURY_MISSILE_4 && ng &&  !Netgame.DoMercury)
	   bash_to_shield (i,"Mercury");
	if (Objects[i].id == POW_CONVERTER && ng &&  !Netgame.DoConverter)
	   bash_to_shield (i,"Converter");
	if (Objects[i].id == POW_AMMO_RACK && ng &&  !Netgame.DoAmmoRack)
	   bash_to_shield (i,"Ammo rack");
	if (Objects[i].id == POW_HEADLIGHT && ng &&  !Netgame.DoHeadlight)
	   bash_to_shield (i,"Headlight");
	if (Objects[i].id == POW_LASER && ng &&  !Netgame.DoLaserUpgrade)
	   bash_to_shield (i,"Laser powerup");
	if (Objects[i].id == POW_HOMING_AMMO_1 && ng &&  !Netgame.DoHoming)
	   bash_to_shield (i,"Homing");
	if (Objects[i].id == POW_HOMING_AMMO_4 && ng &&  !Netgame.DoHoming)
	   bash_to_shield (i,"Homing");
	if (Objects[i].id == POW_QUAD_FIRE && ng &&  !Netgame.DoQuadLasers)
	   bash_to_shield (i,"Quad Lasers");
   if (Objects[i].id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
	   bash_to_shield (i,"Blue flag");
   if (Objects[i].id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
	   bash_to_shield (i,"Red flag");
     }
   }
	
	if (Game_mode & GM_HOARD)
		init_hoard_data();

   if ((Game_mode & GM_CAPTURE) || (Game_mode & GM_HOARD))
	 multi_apply_goal_textures();
  
	multi_sort_kill_list();

	multi_show_player_list();

	ConsoleObject->control_type = CT_FLYING;

	reset_player_object();

}

int Goal_blue_segnum,Goal_red_segnum;

void multi_apply_goal_textures()
{
	int		i,j,tex;
	segment	*seg;
	segment2	*seg2;
 
	for (i=0; i <= Highest_segment_index; i++)
	 {
		seg = &Segments[i];
		seg2 = &Segment2s[i];
 
      if (seg2->special==SEGMENT_IS_GOAL_BLUE)
		 {
			
			Goal_blue_segnum = i;

			if (Game_mode & GM_HOARD)
				tex=find_goal_texture (TMI_GOAL_HOARD);
			else
				tex=find_goal_texture (TMI_GOAL_BLUE);
			
			if (tex>-1)
				for (j = 0; j < 6; j++) {
					int v;
					seg->sides[j].tmap_num=tex;
					for (v=0;v<4;v++)
						seg->sides[j].uvls[v].l = i2f(100);		//max out
				}

			seg2->static_light = i2f(100);	//make static light bright

		 }	
			
      if (seg2->special==SEGMENT_IS_GOAL_RED)
		 {
			Goal_red_segnum = i;

			// Make both textures the same if Hoard mode

			if (Game_mode & GM_HOARD)
				tex=find_goal_texture (TMI_GOAL_HOARD);
			else
				tex=find_goal_texture (TMI_GOAL_RED);
		
			if (tex>-1)
				for (j = 0; j < 6; j++) {
					int v;
					seg->sides[j].tmap_num=tex;
					for (v=0;v<4;v++)
						seg->sides[j].uvls[v].l = i2f(1000);		//max out
				}

			seg2->static_light = i2f(100);	//make static light bright
		 }
  	 }
 }
int find_goal_texture (ubyte t)
 {
  int i;
 
  for (i=0;i<NumTextures;i++)
	if (TmapInfo[i].flags & t)
		return i;

  Int3(); // Hey, there is no goal texture for this PIG!!!!
			 // Edit bitmaps.tbl and designate two textures to be RED and BLUE
			 // goal textures
  return (-1);
 }


void bash_to_shield (int i,char *s)
 {
   int type=Objects[i].id;
  
    mprintf((0, "Bashing %s object #%i to shield.\n",s, i));

	 PowerupsInMine[type]=MaxPowerupsAllowed[type]=0;
				  
    Objects[i].id = POW_SHIELD_BOOST;
    Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
    Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
 }

void multi_set_robot_ai(void)
{
	// Go through the objects array looking for robots and setting
	// them to certain supported types of NET AI behavior.

//      int i;
//
//      for (i = 0; i <= Highest_object_index; i++)
//      {
//              if (Objects[i].type == OBJ_ROBOT) {
//                      Objects[i].ai_info.REMOTE_OWNER = -1;
//                      if (Objects[i].ai_info.behavior == AIB_STATION)
//                              Objects[i].ai_info.behavior = AIB_NORMAL;
//              }
//      }
}

int multi_delete_extra_objects()
{
	int i;
	int nnp=0;
	object *objp;
	
	// Go through the object list and remove any objects not used in
	// 'Anarchy!' games.

	// This function also prints the total number of available multiplayer
	// positions in this level, even though this should always be 8 or more!

	objp = Objects;
	for (i=0;i<=Highest_object_index;i++) {
		if ((objp->type==OBJ_PLAYER) || (objp->type==OBJ_GHOST)) 
			nnp++;
		else if ((objp->type==OBJ_ROBOT) && (Game_mode & GM_MULTI_ROBOTS))
			;
		else if ( (objp->type!=OBJ_NONE) && (objp->type!=OBJ_PLAYER) && (objp->type!=OBJ_POWERUP) && (objp->type!=OBJ_CNTRLCEN) && (objp->type!=OBJ_HOSTAGE) && !(objp->type==OBJ_WEAPON && objp->id==PMINE_ID) ) {
			//	Before deleting object, if it's a robot, drop it's special powerup, if any
			if (objp->type == OBJ_ROBOT)
				if (objp->contains_count && (objp->contains_type == OBJ_POWERUP))
					object_create_egg(objp);
			obj_delete(i);
		}
		objp++;
	}

	return nnp;
}

void change_playernum_to( int new_Player_num )  
{
	if (Player_num > -1)
		memcpy( Players[new_Player_num].callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
	Player_num = new_Player_num;
}

int multi_all_players_alive()
 {
  int i;
  for (i=0;i<N_players;i++)
	{
	 if (PKilledFlags[i] && Players[i].connected)
		return (0);
	}
  return (1);
 }

void multi_initiate_save_game()
{
	uint game_id;
	int i, slot;
	char filename[128];
	char desc[24];

	if ((Endlevel_sequence) || (Control_center_destroyed))
		return;
    
   if (!multi_all_players_alive())
	 {
	  HUD_init_message ("Can't save...all players must be alive!");
	  return;
	 }

//      multi_send_netplayer_stats_request(255);
//      return;

//	stop_time();
	
	slot = state_get_save_file(filename, desc, 1 );
	if (!slot)      {
		//start_time();
		return;
	}
	slot--;

//	start_time();

	// Make a unique game id
	game_id = timer_get_fixed_seconds();
	game_id ^= N_players<<4;
	for (i=0; i<N_players; i++ )
		game_id ^= *(uint *)Players[i].callsign;
	if ( game_id == 0 ) game_id = 1;                // 0 is invalid

	mprintf(( 1, "Game_id = %8x\n", game_id));
	multi_send_save_game(slot, game_id, desc );
	multi_do_frame();
	multi_save_game(slot,game_id, desc );
}

extern int state_get_game_id(char *);

void multi_initiate_restore_game()
{
	int slot;
	char filename[128];

	if ((Endlevel_sequence) || (Control_center_destroyed))
		return;

   if (!multi_all_players_alive())
	 {
	  HUD_init_message ("Can't restore...all players must be alive!");
	  return;
	 }

//	stop_time();
	slot = state_get_restore_file(filename,1);
	if (!slot)      {
		//start_time();
		return;
	}
   state_game_id=state_get_game_id (filename);
   if (!state_game_id)
		return;
  
	slot--;
//	start_time();
	multi_send_restore_game(slot,state_game_id);
	multi_do_frame();
	multi_restore_game(slot,state_game_id);
}

void multi_save_game(ubyte slot, uint id, char *desc)
{
	char filename[128];

	if ((Endlevel_sequence) || (Control_center_destroyed))
		return;

	#ifndef MACINTOSH
	sprintf( filename, "%s.mg%d", Players[Player_num].callsign, slot );
	#else
	sprintf( filename, ":Players:%s.mg%d", Players[Player_num].callsign, slot );
	#endif
	mprintf(( 0, "Save game %x on slot %d\n", id, slot ));
	HUD_init_message( "Saving game #%d, '%s'", slot, desc );
	stop_time();
	state_game_id = id;
	state_save_all_sub(filename, desc, 0 );
}

void multi_restore_game(ubyte slot, uint id)
{
	char filename[128];
	player saved_player;
	int pnum,i;
	int thisid;

	if ((Endlevel_sequence) || (Control_center_destroyed))
		return;

	mprintf(( 0, "Restore game %x from slot %d\n", id, slot ));
	saved_player = Players[Player_num];
	#ifndef MACINTOSH
	sprintf( filename, "%s.mg%d", Players[Player_num].callsign, slot );
	#else
	sprintf( filename, ":Players:%s.mg%d", Players[Player_num].callsign, slot );
	#endif
   
   for (i=0;i<N_players;i++)
		multi_strip_robots(i);
   
 	thisid=state_get_game_id (filename);
   if (thisid!=id)
	 {
		multi_bad_restore ();
		return;
	 }
  
	pnum=state_restore_all_sub( filename, 1, 0 );

	mprintf ((0,"StateId=%d ThisID=%d\n",state_game_id,id));
	
/*        if (state_game_id != id )       {
		// Game doesn't match!!!
		nm_messagebox( "Error", 1, "Ok", "Cannot restore saved game" );
		Game_mode |= GM_GAME_OVER;
		Function_mode = FMODE_MENU;
		longjmp(LeaveGame, 0);
	}

  	change_playernum_to(pnum-1);
	memcpy( Players[Player_num].callsign, saved_player.callsign, CALLSIGN_LEN+1 );
	memcpy( Players[Player_num].net_address, saved_player.net_address, 6 );
	Players[Player_num].connected = saved_player.connected;
	Players[Player_num].n_packets_got  = saved_player.n_packets_got;                                        
	Players[Player_num].n_packets_sent = saved_player.n_packets_sent;                               
	Viewer = ConsoleObject = &Objects[pnum-1]; */
}


void extract_netplayer_stats( netplayer_stats *ps, player * pd )
{
	int i;
	
	ps->flags = INTEL_INT(pd->flags);                                                  // Powerup flags, see below...
	ps->energy = (fix)INTEL_INT(pd->energy);                                                        // Amount of energy remaining.
	ps->shields = (fix)INTEL_INT(pd->shields);                                                      // shields remaining (protection) 
	ps->lives = pd->lives;                                                  // Lives remaining, 0 = game over.
	ps->laser_level = pd->laser_level;                                      //      Current level of the laser.
	ps->primary_weapon_flags=pd->primary_weapon_flags;                                      //      bit set indicates the player has this weapon.
	ps->secondary_weapon_flags=pd->secondary_weapon_flags;                                  //      bit set indicates the player has this weapon.
	for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
		ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
	for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
		ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);

//	memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );        // How much ammo of each type.
//	memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.

	ps->last_score=INTEL_INT(pd->last_score);								// Score at beginning of current level.
	ps->score=INTEL_INT(pd->score);											// Current score.
	ps->cloak_time=(fix)INTEL_INT(pd->cloak_time);							// Time cloaked
	ps->homing_object_dist=(fix)INTEL_INT(pd->homing_object_dist);          //      Distance of nearest homing object.
	ps->invulnerable_time=(fix)INTEL_INT(pd->invulnerable_time);			// Time invulnerable
	ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
	ps->net_killed_total=INTEL_SHORT(pd->net_killed_total);					// Number of times killed total
	ps->net_kills_total=INTEL_SHORT(pd->net_kills_total);					// Number of net kills total
	ps->num_kills_level=INTEL_SHORT(pd->num_kills_level);					// Number of kills this level
	ps->num_kills_total=INTEL_SHORT(pd->num_kills_total);					// Number of kills total
	ps->num_robots_level=INTEL_SHORT(pd->num_robots_level);					// Number of initial robots this level
	ps->num_robots_total=INTEL_SHORT(pd->num_robots_total);					// Number of robots total
	ps->hostages_rescued_total=INTEL_SHORT(pd->hostages_rescued_total);		// Total number of hostages rescued.
	ps->hostages_total=INTEL_SHORT(pd->hostages_total);						// Total number of hostages.
	ps->hostages_on_board=pd->hostages_on_board;							//      Number of hostages on ship.
}

void use_netplayer_stats( player * ps, netplayer_stats *pd )
{
	int i;
	
	ps->flags = INTEL_INT(pd->flags);							// Powerup flags, see below...
	ps->energy = (fix)INTEL_INT((int)pd->energy);				// Amount of energy remaining.
	ps->shields = (fix)INTEL_INT((int)pd->shields);			// shields remaining (protection) 
	ps->lives = pd->lives;									// Lives remaining, 0 = game over.
	ps->laser_level = pd->laser_level;						//	Current level of the laser.
	ps->primary_weapon_flags=pd->primary_weapon_flags;		//	bit set indicates the player has this weapon.
	ps->secondary_weapon_flags=pd->secondary_weapon_flags;	//	bit set indicates the player has this weapon.
	for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
		ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
	for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
		ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);
//	memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );	// How much ammo of each type.
//	memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
	ps->last_score = INTEL_INT(pd->last_score);				// Score at beginning of current level.
	ps->score = INTEL_INT(pd->score);							// Current score.
	ps->cloak_time = (fix)INTEL_INT((int)pd->cloak_time);		// Time cloaked
	ps->homing_object_dist = (fix)INTEL_INT((int)pd->homing_object_dist); //	Distance of nearest homing object.
	ps->invulnerable_time = (fix)INTEL_INT((int)pd->invulnerable_time);	// Time invulnerable
	ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
	ps->net_killed_total = INTEL_SHORT(pd->net_killed_total);	// Number of times killed total
 	ps->net_kills_total = INTEL_SHORT(pd->net_kills_total);	// Number of net kills total
	ps->num_kills_level = INTEL_SHORT(pd->num_kills_level);	// Number of kills this level
 	ps->num_kills_total = INTEL_SHORT(pd->num_kills_total);	// Number of kills total
	ps->num_robots_level = INTEL_SHORT(pd->num_robots_level);	// Number of initial robots this level
	ps->num_robots_total = INTEL_SHORT(pd->num_robots_total);	// Number of robots total
	ps->hostages_rescued_total = INTEL_SHORT(pd->hostages_rescued_total);	// Total number of hostages rescued.
	ps->hostages_total = INTEL_SHORT(pd->hostages_total);		// Total number of hostages.
	ps->hostages_on_board=pd->hostages_on_board;			//	Number of hostages on ship.
}

void multi_send_drop_weapon (int objnum,int seed)
 {
	object *objp;
  int count=0;
	int ammo_count;

	objp = &Objects[objnum];

	ammo_count = objp->ctype.powerup_info.count;

	if (objp->id == POW_OMEGA_WEAPON && ammo_count == F1_0)
		ammo_count = F1_0 - 1;	//make fit in short

	Assert(ammo_count < F1_0);	//make sure fits in short

  multibuf[count++]=(char)MULTI_DROP_WEAPON;
  multibuf[count++]=(char)objp->id;

  *(short *) (multibuf+count)=INTEL_SHORT(Player_num); count += 2;
  *(short *) (multibuf+count)=INTEL_SHORT(objnum); count += 2;
  *(short *) (multibuf+count)=INTEL_SHORT(ammo_count); count += 2;
  *(int *) (multibuf+count)=INTEL_INT(seed);
   
  map_objnum_local_to_local(objnum);
  
  if (Game_mode & GM_NETWORK)
	   PowerupsInMine[objp->id]++;

  multi_send_data(multibuf, 12, 2);
 }

void multi_do_drop_weapon (char *buf)
 {
  int pnum,ammo,objnum,remote_objnum,seed;
	object *objp;
  int powerup_id;

	powerup_id=(int)(buf[1]);
	pnum = INTEL_SHORT(*(short *)(buf+2));
	remote_objnum = INTEL_SHORT(*(short *)(buf+4));
	ammo = INTEL_SHORT(*(ushort *)(buf+6));
	seed = INTEL_INT(*(int *)(buf+8));

	objp = &Objects[Players[pnum].objnum];

	objnum = spit_powerup(objp, powerup_id, seed);

	map_objnum_local_to_remote(objnum, remote_objnum, pnum);                

	if (objnum!=-1)
 		Objects[objnum].ctype.powerup_info.count = ammo;
   
   if (Game_mode & GM_NETWORK)
		PowerupsInMine[powerup_id]++;

  mprintf ((0,"Dropped weapon %d!\n"));
  
 }

void multi_send_guided_info (object *miss,char done)
{
#ifdef MACINTOSH
	shortpos sp;
#endif
  int count=0;

  mprintf ((0,"Sending guided info!\n"));

  multibuf[count++]=(char)MULTI_GUIDED;
  multibuf[count++]=(char)Player_num;
  multibuf[count++]=done;

#ifndef MACINTOSH
  create_shortpos((shortpos *)(multibuf+count), miss,0);
  count+=sizeof(shortpos);
#else
	create_shortpos(&sp, miss, 1);
	memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
	count += 9;
	memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
	count += 14;
#endif

  multi_send_data(multibuf, count, 0);
 }

void multi_do_guided (char *buf)
 {
  char pnum=buf[1];
  int count=3;
  static fun=200;
#ifdef MACINTOSH
	shortpos sp;
#endif

  if (Guided_missile[pnum]==NULL)
   {
    if (++fun>=50)
     {
      mprintf ((0,"Guided missile for %s is NULL!\n",Players[pnum].callsign));
      fun=0;
     }
    return;
   }
  else if (++fun>=50)
   {
    mprintf ((0,"Got guided info for %d (%s)\n",pnum,Players[pnum].callsign));
    fun=0;
   }

  if (buf[2])
   {
	 release_guided_missile(pnum);
	 return;
	}


  if (Guided_missile[pnum]-Objects<0 || Guided_missile[pnum]-Objects > Highest_object_index)
	{	
	 Int3();  // Get Jason immediately!
	 return;
	}

#ifndef MACINTOSH	
  extract_shortpos(Guided_missile[pnum], (shortpos *)(buf+count),0); 
#else
	memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + count), 9);
	memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + count + 9), 14);
	extract_shortpos(Guided_missile[pnum], &sp, 1);
#endif

  count+=sizeof (shortpos);

  update_object_seg(Guided_missile[pnum]);
 }

void multi_send_stolen_items ()
 {
  int i,count=1;
  multibuf[0]=MULTI_STOLEN_ITEMS;
  
  for (i=0;i<MAX_STOLEN_ITEMS;i++)
   {
    multibuf[i+1]=Stolen_items[i];
	 mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
    count++;      // So I like to break my stuff into smaller chunks, so what?
   }
  mprintf ((0,"\n"));
  multi_send_data(multibuf, count, 1);
 }

void multi_do_stolen_items (char *buf)
 {
  int i;

  mprintf ((0,"Recieved a stolen item packet...\n"));

  for (i=0;i<MAX_STOLEN_ITEMS;i++)
  {
   Stolen_items[i]=buf[i+1];
	mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
  }
  mprintf ((0,"\n"));
 }

extern void network_send_important_packet (char *,int);

void multi_send_wall_status (int wallnum,ubyte type,ubyte flags,ubyte state)
{
  int count=0;
  multibuf[count]=MULTI_WALL_STATUS;        count++;
  *(short *)(multibuf+count)=INTEL_SHORT(wallnum);   count+=2;
  multibuf[count]=type;                 count++;
  multibuf[count]=flags;                count++;
  multibuf[count]=state;                count++;

 /* if (Game_mode & GM_NETWORK)
	{
	  network_send_important_packet (multibuf,count);
	  network_send_important_packet (multibuf,count);
	} 
  else*/
	{
	  multi_send_data(multibuf, count, 1); // twice, just to be sure
     multi_send_data(multibuf, count, 1);
	}
}
void multi_send_wall_status_specific (int pnum,int wallnum,ubyte type,ubyte flags,ubyte state)
{
  // Send wall states a specific rejoining player
 
  int count=0;

  Assert (Game_mode & GM_NETWORK);
//  Assert (pnum>-1 && pnum<N_players);
  
  multibuf[count]=MULTI_WALL_STATUS;        count++;
  *(short *)(multibuf+count)=INTEL_SHORT(wallnum);   count+=2;
  multibuf[count]=type;                 count++;
  multibuf[count]=flags;                count++;
  multibuf[count]=state;                count++;

  network_send_naked_packet(multibuf, count,pnum); // twice, just to be sure
  network_send_naked_packet(multibuf, count,pnum);
}

void multi_do_wall_status (char *buf)
 {
  short wallnum;
  ubyte flag,type,state;

  wallnum=INTEL_SHORT( *(short *)(buf+1) );
  type=buf[3];
  flag=buf[4];
  state=buf[5];

  Assert (wallnum>=0);
  Walls[wallnum].type=type;
  Walls[wallnum].flags=flag;
//  Assert(state <= 4);
  Walls[wallnum].state=state; 
  
  if (Walls[wallnum].type==WALL_OPEN)
	{  
	  digi_kill_sound_linked_to_segment(Walls[wallnum].segnum,Walls[wallnum].sidenum,SOUND_FORCEFIELD_HUM);
 //  digi_kill_sound_linked_to_segment(csegp-Segments,cside,SOUND_FORCEFIELD_HUM);
   }

	
//  mprintf ((0,"Got a walls packet.\n"));
 }

void multi_send_jason_cheat (int num)
 {
  num=num;
  return;
 }

void multi_send_kill_goal_counts()
 {
  int i,count=1;
  multibuf[0]=MULTI_KILLGOALS;
  
  for (i=0;i<MAX_PLAYERS;i++)
   {
    *(char *)(multibuf+count)=(char)Players[i].KillGoalCount;
    count++;
   }

  mprintf ((0,"MULTI: Sending KillGoalCounts...\n"));
  multi_send_data(multibuf, count, 1);
 }

void multi_do_kill_goal_counts(char *buf)
 {
  int i,count=1;
  
  for (i=0;i<MAX_PLAYERS;i++)
   {
    Players[i].KillGoalCount=*(char *)(buf+count);
    mprintf ((0,"KGC: %s has %d kills!\n",Players[i].callsign,Players[i].KillGoalCount));
    count++;
   }

 }

void multi_send_heartbeat ()
 {
  if (!Netgame.PlayTimeAllowed)
	return;
 
  multibuf[0]=MULTI_HEARTBEAT;
  *(fix *)(multibuf+1)=(fix)INTEL_INT(ThisLevelTime);
  multi_send_data(multibuf, 5, 0);
 }

void multi_do_heartbeat (char *buf)
 {
  fix num;

  num=(fix)INTEL_INT(*(int *)(buf+1));

  ThisLevelTime=num;
 }

void multi_check_for_killgoal_winner ()
 {
  int i,best=0,bestnum=0;
  object *objp;
 
  
  if (Control_center_destroyed)	
	return;
 
  for (i=0;i<N_players;i++)
   {
    if (Players[i].KillGoalCount>best)
     {
      best=Players[i].KillGoalCount;
      bestnum=i;
     }
   }

  if (bestnum==Player_num)
   {
    HUD_init_message("You have the best score at %d kills!",best);
//    Players[Player_num].shields=i2f(200);
   }
  else

   HUD_init_message ("%s has the best score with %d kills!",Players[bestnum].callsign,best);

  HUD_init_message ("The control center has been destroyed!");
 
  objp=obj_find_first_of_type (OBJ_CNTRLCEN);
  net_destroy_controlcen (objp);
 }
  
void multi_send_seismic (fix start,fix end)
 {
  int count=1; 
  
  multibuf[0]=MULTI_SEISMIC;
  *(fix *)(multibuf+count)=(fix)INTEL_INT(start); count+=(sizeof(fix));
  *(fix *)(multibuf+count)=(fix)INTEL_INT(end); count+=(sizeof(fix));
  
  multi_send_data(multibuf, count, 1);
 }

extern fix Seismic_disturbance_start_time;
extern fix Seismic_disturbance_end_time;
     
void multi_do_seismic (char *buf)
 {
  Seismic_disturbance_start_time=(fix)INTEL_INT( *(int *)(buf+1) );
  Seismic_disturbance_end_time=(fix)INTEL_INT( *(int *)(buf+5) );
  digi_play_sample (SOUND_SEISMIC_DISTURBANCE_START, F1_0);
 }

void multi_send_light (int segnum,ubyte val)
 {
  int count=1,i;
  multibuf[0]=MULTI_LIGHT;
  *(int *)(multibuf+count)=INTEL_INT(segnum); count+=(sizeof(int));
  *(char *)(multibuf+count)=val; count++;
  for (i=0;i<6;i++)
	{
    //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
	 *(short *)(multibuf+count)=INTEL_SHORT(Segments[segnum].sides[i].tmap_num2); count+=2;
	}
   multi_send_data(multibuf, count, 1);
 }
void multi_send_light_specific (int pnum,int segnum,ubyte val)
 {
  int count=1,i;

  Assert (Game_mode & GM_NETWORK);
//  Assert (pnum>-1 && pnum<N_players);
 
  multibuf[0]=MULTI_LIGHT;
  *(int *)(multibuf+count)=INTEL_INT(segnum); count+=(sizeof(int));
  *(char *)(multibuf+count)=val; count++;

  for (i=0;i<6;i++)
	{
    //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
	 *(short *)(multibuf+count)=INTEL_SHORT(Segments[segnum].sides[i].tmap_num2); count+=2;
	}
   network_send_naked_packet(multibuf, count, pnum);
 }

void multi_do_light (char *buf)
 {
  int i;
  int seg=INTEL_INT(*(int *)(buf+1));
  ubyte sides=*(char *)(buf+5);

  for (i=0;i<6;i++)
	{
	 if ((sides & (1<<i)))
     {
      subtract_light (seg,i);
      Segments[seg].sides[i].tmap_num2=INTEL_SHORT( *(short *)(buf+(6+(2*i))) );
  //            mprintf ((0,"Got %d!\n",Segments[seg].sides[i].tmap_num2));
	  }     
   }
 }

//@@void multi_send_start_trigger(int triggernum)
//@@{
//@@	// Send an even to trigger something in the mine
//@@	
//@@	int count = 0;
//@@	
//@@	multibuf[count] = MULTI_START_TRIGGER;                          count += 1;
//@@	multibuf[count] = Player_num;                                   count += 1;
//@@	multibuf[count] = (ubyte)triggernum;            count += 1;
//@@   
//@@//   mprintf ((0,"Sending start trigger %d\n",triggernum));
//@@	multi_send_data(multibuf, count, 2);
//@@}
//@@void multi_do_start_trigger(char *buf)
//@@{
//@@	int pnum = buf[1];
//@@	int trigger = buf[2];
//@@   
//@@//   mprintf ((0,"MULTI doing start trigger!\n"));
//@@
//@@	if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
//@@	{
//@@		Int3(); // Got trigger from illegal playernum
//@@		return;
//@@	}
//@@	if ((trigger < 0) || (trigger >= Num_triggers))
//@@	{
//@@		Int3(); // Illegal trigger number in multiplayer
//@@		return;
//@@	}
//@@
//@@  if (!(Triggers[trigger].flags & TF_SPRUNG))
//@@	check_trigger_sub(trigger, pnum,0);
//@@}


void multi_do_flags (char *buf)
{
	char pnum=buf[1];
	uint flags=INTEL_INT( *(uint *)(buf+2) );

	if (pnum!=Player_num)
		Players[pnum].flags=flags;
}

void multi_send_flags (char pnum)
 {
  multibuf[0]=MULTI_FLAGS;
  multibuf[1]=pnum;
  *(uint *)(multibuf+2)=INTEL_INT(Players[pnum].flags);
  
  multi_send_data(multibuf, 6, 1);
 } 
  
void multi_send_drop_blobs (char pnum)
 {
  multibuf[0]=MULTI_DROP_BLOB;
  multibuf[1]=pnum;

  multi_send_data(multibuf, 2, 0);
 }

void multi_do_drop_blob (char *buf)
 {
   char pnum=buf[1];
   drop_afterburner_blobs (&Objects[Players[pnum].objnum], 2, i2f(5)/2, -1);
 }

void multi_send_powerup_update ()
 {
  int i; 	

	
  multibuf[0]=MULTI_POWERUP_UPDATE;
  for (i=0;i<MAX_POWERUP_TYPES;i++)	
   multibuf[i+1]=MaxPowerupsAllowed[i];

  multi_send_data(multibuf, MAX_POWERUP_TYPES+1, 1);
 }
void multi_do_powerup_update (char *buf)
 {
  int i;

  for (i=0;i<MAX_POWERUP_TYPES;i++)
	if (buf[i+1]>MaxPowerupsAllowed[i])
		MaxPowerupsAllowed[i]=buf[i+1];
 }

extern active_door ActiveDoors[];
extern int Num_open_doors;						// Number of open doors
  
void multi_send_active_door (char i)
{
	int count;
	
	multibuf[0]=MULTI_ACTIVE_DOOR;
	multibuf[1]=i;
	multibuf[2]=Num_open_doors;	
	count = 3;
#ifndef MACINTOSH
	memcpy ((char *)(multibuf[3]),&ActiveDoors[i],sizeof(struct active_door));
	count += sizeof(active_door);
#else
	*(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].n_parts);					count += 4;
	*(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[0]);	count += 2;
	*(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[1]);	count += 2;
	*(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[0]);		count += 2;
	*(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[1]);		count += 2;
	*(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].time);					count += 4;
#endif
//	multi_send_data (multibuf,sizeof(struct active_door)+3,1);
	multi_send_data (multibuf,count,1);
}

void multi_do_active_door (char *buf)
{
	int count;
	char i=multibuf[1];
	Num_open_doors=buf[2];
	
	count = 3;
#ifndef MACINTOSH
	memcpy (&ActiveDoors[i],buf+count,sizeof(struct active_door));
#else
	ActiveDoors[i].n_parts = INTEL_INT( *(int *)(buf+count) );				count += 4;
	ActiveDoors[i].front_wallnum[0] = INTEL_SHORT( *(short *)(buf+count) );	count +=2;
	ActiveDoors[i].front_wallnum[1] = INTEL_SHORT( *(short *)(buf+count) );	count +=2;
	ActiveDoors[i].back_wallnum[0] = INTEL_SHORT( *(short *)(buf+count) );	count +=2;
	ActiveDoors[i].back_wallnum[1] = INTEL_SHORT( *(short *)(buf+count) );	count +=2;
	ActiveDoors[i].time = INTEL_INT( *(int *)(buf+count) );					count += 4;
#endif
}	

void multi_send_sound_function (char whichfunc,char sound)
{
	int count=0;
  
	multibuf[0]=MULTI_SOUND_FUNCTION;  count++;
	multibuf[1]=Player_num; count++;
	multibuf[2]=whichfunc;  count++;
	#ifndef MACINTOSH
	*(uint *)(multibuf+count)=sound;	count++;
	#else
	multibuf[3] = sound; count++;		// this would probably work on the PC as well.  Jason?
	#endif
	multi_send_data (multibuf,4,0);
}

#define AFTERBURNER_LOOP_START	20098
#define AFTERBURNER_LOOP_END		25776

void multi_do_sound_function (char *buf)
{
   // for afterburner
  
	char pnum,whichfunc;
	int sound; 

   if (Players[Player_num].connected!=1)
		return;	

   pnum=buf[1];
	whichfunc=buf[2];
	sound=buf[3];
	
	if (whichfunc==0)
		digi_kill_sound_linked_to_object (Players[pnum].objnum);
	else if (whichfunc==3)
	   digi_link_sound_to_object3( sound, Players[pnum].objnum, 1,F1_0, i2f(256), AFTERBURNER_LOOP_START, AFTERBURNER_LOOP_END);
}  

void multi_send_capture_bonus (char pnum)
{
  Assert (Game_mode & GM_CAPTURE);
  
  multibuf[0]=MULTI_CAPTURE_BONUS;
  multibuf[1]=pnum;

  multi_send_data (multibuf,2,1); 
  multi_do_capture_bonus (multibuf);
}
void multi_send_orb_bonus (char pnum)
{
  Assert (Game_mode & GM_HOARD);
  
  multibuf[0]=MULTI_ORB_BONUS;
  multibuf[1]=pnum;
  multibuf[2]=Players[Player_num].secondary_ammo[PROXIMITY_INDEX];

  multi_send_data (multibuf,3,1); 
  multi_do_orb_bonus (multibuf);
}
void multi_do_capture_bonus(char *buf)
{
	// Figure out the results of a network kills and add it to the
	// appropriate player's tally.

   char pnum=buf[1];
   int TheGoal;

	kmatrix_kills_changed = 1;
   
   if (pnum==Player_num)
		HUD_init_message("You have Scored!");
	else
		HUD_init_message("%s has Scored!",Players[pnum].callsign);

	if (pnum==Player_num)
		digi_play_sample (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
	else if (get_team(pnum)==TEAM_RED)
		digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
	else
		digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);

	Players[pnum].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear capture flag

	team_kills[get_team(pnum)] += 5;
	Players[pnum].net_kills_total += 5;
	Players[pnum].KillGoalCount+=5;

  if (Netgame.KillGoal>0)
	{
    TheGoal=Netgame.KillGoal*5;

	 if (Players[pnum].KillGoalCount>=TheGoal)
	  {
	   if (pnum==Player_num)
	    {
	     HUD_init_message("You reached the kill goal!");
	     Players[Player_num].shields=i2f(200);
	    }  
	   else
	    HUD_init_message ("%s has reached the kill goal!",Players[pnum].callsign);

	   HUD_init_message ("The control center has been destroyed!");
      net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
	  }
	}

	multi_sort_kill_list();
	multi_show_player_list();
}

int GetOrbBonus (char num)
 {
  int bonus;

  bonus=num*(num+1)/2;
  return (bonus);
 }

void multi_do_orb_bonus(char *buf)
{
	// Figure out the results of a network kills and add it to the
	// appropriate player's tally.

   char pnum=buf[1];
   int TheGoal;
	int bonus=GetOrbBonus (buf[2]);

	kmatrix_kills_changed = 1;
   
   if (pnum==Player_num)
		HUD_init_message("You have scored %d points!",bonus);
	else
		HUD_init_message("%s has scored with %d orbs!",Players[pnum].callsign,buf[2]);

	if (pnum==Player_num)
		digi_start_sound_queued (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
	else if (Game_mode & GM_TEAM)
	 {
	  if (get_team(pnum)==TEAM_RED)
		digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
	  else
		digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);
	 }
	else
		digi_play_sample (SOUND_OPPONENT_HAS_SCORED,F1_0*2);

   if (bonus>PhallicLimit)
	{
	  if (pnum==Player_num)
	 	HUD_init_message ("You have the record with %d points!",bonus);
	  else
	  	HUD_init_message ("%s has the record with %d points!",Players[pnum].callsign,bonus);
     digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
	  PhallicMan=pnum;
	  PhallicLimit=bonus;
   }

	Players[pnum].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear orb flag
	
	team_kills[get_team(pnum)] += bonus;
	Players[pnum].net_kills_total += bonus;
	Players[pnum].KillGoalCount+=bonus;

	team_kills[get_team(pnum)]%=1000;
	Players[pnum].net_kills_total%=1000;
	Players[pnum].KillGoalCount%=1000;

  if (Netgame.KillGoal>0)
	{
    TheGoal=Netgame.KillGoal*5;

	 if (Players[pnum].KillGoalCount>=TheGoal)
	  {
	   if (pnum==Player_num)
	    {
	     HUD_init_message("You reached the kill goal!");
	     Players[Player_num].shields=i2f(200);
	    }  
	   else
	    HUD_init_message ("%s has reached the kill goal!",Players[pnum].callsign);

	   HUD_init_message ("The control center has been destroyed!");
      net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
	  }
	}
	multi_sort_kill_list();
	multi_show_player_list();
}

void multi_send_got_flag (char pnum)
{
  multibuf[0]=MULTI_GOT_FLAG;
  multibuf[1]=pnum;
  
  digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);

  multi_send_data (multibuf,2,1);
  multi_send_flags (Player_num);
}

int SoundHacked=0;
digi_sound ReversedSound;

void multi_send_got_orb (char pnum)
{
  multibuf[0]=MULTI_GOT_ORB;
  multibuf[1]=pnum;
  
  digi_play_sample (SOUND_YOU_GOT_ORB,F1_0*2);

  multi_send_data (multibuf,2,1);
  multi_send_flags (Player_num);
}

void multi_do_got_flag (char *buf)
{
	char pnum=buf[1];

   if (pnum==Player_num)
		digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);
	else if (get_team(pnum)==TEAM_RED)
		digi_start_sound_queued (SOUND_HUD_RED_GOT_FLAG,F1_0*2);
	else 
		digi_start_sound_queued (SOUND_HUD_BLUE_GOT_FLAG,F1_0*2);
   Players[pnum].flags|=PLAYER_FLAGS_FLAG;
	HUD_init_message ("%s picked up a flag!",Players[pnum].callsign);
}
void multi_do_got_orb (char *buf)
{
	char pnum=buf[1];

   Assert (Game_mode & GM_HOARD);

   if (Game_mode & GM_TEAM)
	 {  
	  if (get_team(pnum)==get_team(Player_num))
	  	digi_play_sample (SOUND_FRIEND_GOT_ORB,F1_0*2);
	  else 
		digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);
    }
   else 
	 digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);

   Players[pnum].flags|=PLAYER_FLAGS_FLAG;
	HUD_init_message ("%s picked up an orb!",Players[pnum].callsign);
}


void DropOrb ()
{
	int objnum,seed;

	if (!(Game_mode & GM_HOARD))
	 Int3(); // How did we get here? Get Leighton!

	if (!Players[Player_num].secondary_ammo[PROXIMITY_INDEX])
	{
   	HUD_init_message("No orbs to drop!");
		return;
	}

	seed = rand();

   objnum = spit_powerup(ConsoleObject,POW_HOARD_ORB,seed);
 	 
   if (objnum<0)
		return;

	HUD_init_message("Orb dropped!");
	digi_play_sample (SOUND_DROP_WEAPON,F1_0);

	if ((Game_mode & GM_HOARD) && objnum>-1)
		multi_send_drop_flag(objnum,seed); 
	
	Players[Player_num].secondary_ammo[PROXIMITY_INDEX]--;

   // If empty, tell everyone to stop drawing the box around me
	if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]==0)
	 multi_send_flags (Player_num);
}

void DropFlag ()
{
	int objnum,seed;

	if (!(Game_mode & GM_CAPTURE) && !(Game_mode & GM_HOARD))
		return;
	if (Game_mode & GM_HOARD)
	{
		DropOrb();
		return;
   }	

	if (!(Players[Player_num].flags & PLAYER_FLAGS_FLAG))
	{
   	HUD_init_message("No flag to drop!");
		return;
	}


	HUD_init_message("Flag dropped!");
	digi_play_sample (SOUND_DROP_WEAPON,F1_0);

	seed = rand();

   if (get_team (Player_num)==TEAM_RED)
	 objnum = spit_powerup(ConsoleObject,POW_FLAG_BLUE,seed);
	else
	 objnum = spit_powerup(ConsoleObject,POW_FLAG_RED,seed);
	 
   if (objnum<0)
		return;

	if ((Game_mode & GM_CAPTURE) && objnum>-1)
		multi_send_drop_flag(objnum,seed);

	Players[Player_num].flags &=~(PLAYER_FLAGS_FLAG);
}


void multi_send_drop_flag (int objnum,int seed)
{
	object *objp;
	int count=0;

	objp = &Objects[objnum];

	multibuf[count++]=(char)MULTI_DROP_FLAG;
	multibuf[count++]=(char)objp->id;
	
	*(short *) (multibuf+count)=INTEL_SHORT(Player_num); count += 2;
	*(short *) (multibuf+count)=INTEL_SHORT(objnum); count += 2;
	*(short *) (multibuf+count)=INTEL_SHORT(objp->ctype.powerup_info.count); count += 2;
	*(int *) (multibuf+count)=INTEL_INT(seed);
   
	map_objnum_local_to_local(objnum);
  
   if (!(Game_mode & GM_HOARD))
		if (Game_mode & GM_NETWORK)
			PowerupsInMine[objp->id]++;

  multi_send_data(multibuf, 12, 2);
}

void multi_do_drop_flag (char *buf)
{
	int pnum,ammo,objnum,remote_objnum,seed;
	object *objp;
	int powerup_id;

	powerup_id=buf[1];
	pnum=INTEL_SHORT( *(short *)(buf+2) );
	remote_objnum=INTEL_SHORT( *(short *)(buf+4) );
	ammo=INTEL_SHORT( *(short *)(buf+6) );
	seed=INTEL_INT( *(int *)(buf+8) );

	objp = &Objects[Players[pnum].objnum];

	objnum = spit_powerup(objp, powerup_id, seed);

	map_objnum_local_to_remote(objnum, remote_objnum, pnum);                

	if (objnum!=-1)
 		Objects[objnum].ctype.powerup_info.count = ammo;
   
	if (!(Game_mode & GM_HOARD))
	{
		if (Game_mode & GM_NETWORK)
			PowerupsInMine[powerup_id]++;
	   Players[pnum].flags &= ~(PLAYER_FLAGS_FLAG);
 	}
	mprintf ((0,"Dropped flag %d!\n"));
  
}

void multi_bad_restore ()
{
	Function_mode = FMODE_MENU;
	nm_messagebox(NULL, 1, TXT_OK, 
"A multi-save game was restored\nthat you are missing or does not\nmatch that of the others.\nYou must rejoin if you wish to\ncontinue.");
	Function_mode = FMODE_GAME;
	multi_quit_game = 1;
	multi_leave_menu = 1;
	multi_reset_stuff();
}

extern int robot_controlled[MAX_ROBOTS_CONTROLLED];
extern int robot_agitation[MAX_ROBOTS_CONTROLLED];
extern fix robot_controlled_time[MAX_ROBOTS_CONTROLLED];
extern fix robot_last_send_time[MAX_ROBOTS_CONTROLLED];
extern fix robot_last_message_time[MAX_ROBOTS_CONTROLLED];
extern int robot_send_pending[MAX_ROBOTS_CONTROLLED];
extern int robot_fired[MAX_ROBOTS_CONTROLLED];
extern byte robot_fire_buf[MAX_ROBOTS_CONTROLLED][18+3];

extern void network_send_naked_packet (char *,short,int);

void multi_send_robot_controls (char pnum)
 {
  int count=2;
  
  mprintf ((0,"Sending ROBOT_CONTROLS!!!\n"));
 
  multibuf[0]=MULTI_ROBOT_CONTROLS;
  multibuf[1]=pnum;
  memcpy (&(multibuf[count]),&robot_controlled,MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&(multibuf[count]),&robot_agitation,MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&(multibuf[count]),&robot_controlled_time,MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&(multibuf[count]),&robot_last_send_time,MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&(multibuf[count]),&robot_last_message_time,MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&(multibuf[count]),&robot_send_pending,MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&(multibuf[count]),&robot_fired,MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);

  network_send_naked_packet (multibuf,142,pnum);
 }
void multi_do_robot_controls(char *buf)
 {
  int count=2;

  mprintf ((0,"Recieved ROBOT_CONTROLS!!!\n"));
 
  if (buf[1]!=Player_num)
	{
    Int3(); // Get Jason!  Recieved a coop_sync that wasn't ours!
    return;
   }

  memcpy (&robot_controlled,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&robot_agitation,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&robot_controlled_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&robot_last_send_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&robot_last_message_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&robot_send_pending,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
  memcpy (&robot_fired,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
  count+=(MAX_ROBOTS_CONTROLLED*4);
 }

#define POWERUPADJUSTS 5
int PowerupAdjustMapping[]={11,19,39,41,44};

int multi_powerup_is_4pack (int id)
 {
  int i;
 
  for (i=0;i<POWERUPADJUSTS;i++)
   if (id==PowerupAdjustMapping[i])
    return (1);
  return (0);
 } 

int multi_powerup_is_allowed(int id)
 {
  	if (id == POW_INVULNERABILITY && !Netgame.DoInvulnerability)
	 return (0);
	if (id == POW_CLOAK && !Netgame.DoCloak) 
	 return (0);
	if (id == POW_AFTERBURNER && !Netgame.DoAfterburner)
	 return (0);
	if (id == POW_FUSION_WEAPON &&  !Netgame.DoFusions)
	 return (0);
	if (id == POW_PHOENIX_WEAPON && !Netgame.DoPhoenix)
	 return (0);
	if (id == POW_HELIX_WEAPON && !Netgame.DoHelix)
	 return (0);
	if (id == POW_MEGA_WEAPON && !Netgame.DoMegas)
	 return (0);
	if (id == POW_SMARTBOMB_WEAPON && !Netgame.DoSmarts)
	 return (0);
	if (id == POW_GAUSS_WEAPON && !Netgame.DoGauss)
	 return (0);
	if (id == POW_VULCAN_WEAPON && !Netgame.DoVulcan)
	 return (0);
	if (id == POW_PLASMA_WEAPON && !Netgame.DoPlasma)
	 return (0);
	if (id == POW_OMEGA_WEAPON && !Netgame.DoOmega)
	 return (0);
	if (id == POW_SUPER_LASER && !Netgame.DoSuperLaser)
	 return (0);
	if (id == POW_PROXIMITY_WEAPON && !Netgame.DoProximity)
	 return (0);
	if (id==POW_VULCAN_AMMO && (!Netgame.DoVulcan && !Netgame.DoGauss))
	 return (0);
	if (id == POW_SPREADFIRE_WEAPON && !Netgame.DoSpread)
	 return (0);
	if (id == POW_SMART_MINE && !Netgame.DoSmartMine)
	 return (0);
	if (id == POW_SMISSILE1_1 &&  !Netgame.DoFlash)
	 return (0);
	if (id == POW_SMISSILE1_4 &&  !Netgame.DoFlash)
	 return (0);
	if (id == POW_GUIDED_MISSILE_1 &&  !Netgame.DoGuided)
	 return (0);
	if (id == POW_GUIDED_MISSILE_4 &&  !Netgame.DoGuided)
	 return (0);
	if (id == POW_EARTHSHAKER_MISSILE &&  !Netgame.DoEarthShaker)
	 return (0);
	if (id == POW_MERCURY_MISSILE_1 &&  !Netgame.DoMercury)
	 return (0);
	if (id == POW_MERCURY_MISSILE_4 &&  !Netgame.DoMercury)
	 return (0);
	if (id == POW_CONVERTER &&  !Netgame.DoConverter)
	 return (0);
	if (id == POW_AMMO_RACK &&  !Netgame.DoAmmoRack)
	 return (0);
	if (id == POW_HEADLIGHT &&  !Netgame.DoHeadlight)
	 return (0);
	if (id == POW_LASER &&  !Netgame.DoLaserUpgrade)
	 return (0);
	if (id == POW_HOMING_AMMO_1 &&  !Netgame.DoHoming)
	 return (0);
	if (id == POW_HOMING_AMMO_4 &&  !Netgame.DoHoming)
	 return (0);
	if (id == POW_QUAD_FIRE &&  !Netgame.DoQuadLasers)
	 return (0);
   if (id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
	 return (0);
   if (id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
	 return (0);

  return (1);
 }

void multi_send_finish_game ()
 {
  multibuf[0]=MULTI_FINISH_GAME;
  multibuf[1]=Player_num;

  multi_send_data (multibuf,2,1);
 }


extern void do_final_boss_hacks();
void multi_do_finish_game (char *buf)
 {
  if (buf[0]!=MULTI_FINISH_GAME)
	return;
 
  if (Current_level_num!=Last_level)
	return;

  do_final_boss_hacks();
 }

void multi_send_trigger_specific (char pnum,char trig)
 {
	multibuf[0] = MULTI_START_TRIGGER;
   multibuf[1] = trig;
   
	network_send_naked_packet(multibuf, 2, pnum);
 }
void multi_do_start_trigger (char *buf)
 {  
  Triggers[buf[1]].flags |=TF_DISABLED;
 }

extern int GetMyNetRanking();
extern char *RankStrings[];

void multi_add_lifetime_kills ()
 {  
  // This function adds a kill to lifetime stats of this player, and possibly
  // gives a promotion.  If so, it will tell everyone else
	
  int oldrank;

  if (!Game_mode & GM_NETWORK)
	return;
 
  oldrank=GetMyNetRanking();
  
  Netlife_kills++;

  if (oldrank!=GetMyNetRanking())
   {
  	 multi_send_ranking();
    if (!FindArg("-norankings"))
	  {
  		 HUD_init_message ("You have been promoted to %s!",RankStrings[GetMyNetRanking()]);
   	 digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
	    NetPlayers.players[Player_num].rank=GetMyNetRanking();
	  }
	}
  write_player_file();	
 } 

void multi_add_lifetime_killed ()
 {  
  // This function adds a "killed" to lifetime stats of this player, and possibly
  // gives a demotion.  If so, it will tell everyone else

  int oldrank;

  if (!Game_mode & GM_NETWORK)
	return;
 
  oldrank=GetMyNetRanking();
  
  Netlife_killed++;

  if (oldrank!=GetMyNetRanking())
   {
	  multi_send_ranking();
	  NetPlayers.players[Player_num].rank=GetMyNetRanking();
	  
     if (!FindArg("-norankings"))	
		  HUD_init_message ("You have been demoted to %s!",RankStrings[GetMyNetRanking()]);
  
	}	
  write_player_file();	

 } 
   
void multi_send_ranking ()
 {
  multibuf[0]=(char)MULTI_RANK;
  multibuf[1]=(char)Player_num;
  multibuf[2]=(char)GetMyNetRanking();
  
  multi_send_data (multibuf,3,1);
 }

void multi_do_ranking (char *buf)
 {
  char rankstr[20];
  char pnum=buf[1];
  char rank=buf[2];

  if (NetPlayers.players[pnum].rank<rank)
	 strcpy (rankstr,"promoted");
  else if (NetPlayers.players[pnum].rank>rank)
	 strcpy (rankstr,"demoted");
  else
	 return;

  NetPlayers.players[pnum].rank=rank;

  if (!FindArg("-norankings"))
	  HUD_init_message ("%s has been %s to %s!",Players[pnum].callsign,rankstr,RankStrings[rank]);
 }
void multi_send_modem_ping ()
{
  multibuf[0]=MULTI_MODEM_PING;
  multi_send_data (multibuf,1,1);
}
void multi_send_modem_ping_return ()
 {
  multibuf[0]=MULTI_MODEM_PING_RETURN;
  multi_send_data (multibuf,1,1);
 }

void  multi_do_modem_ping_return ()
 {
  if (PingLaunchTime==0)
	{
	 mprintf ((0,"Got invalid PING RETURN from opponent!\n"));
    return;
   }
  
  PingReturnTime=timer_get_fixed_seconds();

  HUD_init_message ("Ping time for opponent is %d ms!",f2i(fixmul(PingReturnTime-PingLaunchTime,i2f(1000))));
  PingLaunchTime=0;
 }


void multi_quick_sound_hack (int num) 
 {
   int length,i;
  	num = digi_xlat_sound(num);
   length=GameSounds[num].length;
   ReversedSound.data=(ubyte *)malloc (length);
   ReversedSound.length=length;
      
   for (i=0;i<length;i++)
	 ReversedSound.data[i]=GameSounds[num].data[length-i-1];

   SoundHacked=1;
 }

void multi_send_play_by_play (int num,int spnum,int dpnum)
 {
  if (!(Game_mode & GM_HOARD))
	return;
 
  return;
  multibuf[0]=MULTI_PLAY_BY_PLAY;
  multibuf[1]=(char)num; 
  multibuf[2]=(char)spnum;
  multibuf[3]=(char)dpnum;
  multi_send_data (multibuf,4,1);
  multi_do_play_by_play (multibuf);
 }
void multi_do_play_by_play (char *buf)
{
	int whichplay=buf[1];
	int spnum=buf[2];
	int dpnum=buf[3];

	if (!(Game_mode & GM_HOARD))
  	{
	   Int3(); // Get Leighton, something bad has happened.
		return;
   }

	switch (whichplay)
	{
		case 0: // Smacked!
       HUD_init_message ("Ouch! %s has been smacked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
		 break;
		case 1: // Spanked!
       HUD_init_message ("Haha! %s has been spanked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
		 break;
		default:
			Int3(); 
	}
}
  
///
/// CODE TO LOAD HOARD DATA
///

#include "cfile.h"
#include "effects.h"
#include "mem.h"

init_bitmap(grs_bitmap *bm,int w,int h,int flags,ubyte *data)
{
	bm->bm_x = bm->bm_y = 0;
	bm->bm_w = bm->bm_rowsize = w;
	bm->bm_h = h;
	bm->bm_type = BM_LINEAR;
	bm->bm_flags = flags;
	bm->bm_data = data;
	bm->bm_handle = 0;
	bm->avg_color = 0;
}

grs_bitmap Orb_icons[2];

int Hoard_goal_eclip;

init_hoard_data()
{
	static int first_time=1;
	static int orb_vclip;
	int n_orb_frames,n_goal_frames;
	int orb_w,orb_h;
	int icon_w,icon_h;
	ubyte palette[256*3];
	CFILE *ifile;
	int i,save_pos;
	extern int Num_bitmap_files,Num_effects,Num_sound_files;

	ifile = cfopen("hoard.ham","rb");
	if (ifile == NULL)
		Error("can't open <hoard.dat>");

	n_orb_frames = cfile_read_short(ifile);
	orb_w = cfile_read_short(ifile);
	orb_h = cfile_read_short(ifile);
	save_pos = cftell(ifile);
	cfseek(ifile,sizeof(palette)+n_orb_frames*orb_w*orb_h,SEEK_CUR);
	n_goal_frames = cfile_read_short(ifile);
	cfseek(ifile,save_pos,SEEK_SET);

	if (first_time) {
		ubyte *bitmap_data;
		int bitmap_num=Num_bitmap_files;

		//Allocate memory for bitmaps
		MALLOC( bitmap_data, ubyte, n_orb_frames*orb_w*orb_h + n_goal_frames*64*64 );

		//Create orb vclip
		orb_vclip = Num_vclips++;
		Assert(Num_vclips <= VCLIP_MAXNUM);
		Vclip[orb_vclip].play_time = F1_0/2;
		Vclip[orb_vclip].num_frames = n_orb_frames;
		Vclip[orb_vclip].frame_time = Vclip[orb_vclip].play_time / Vclip[orb_vclip].num_frames;
		Vclip[orb_vclip].flags = 0;
		Vclip[orb_vclip].sound_num = -1;
		Vclip[orb_vclip].light_value = F1_0;
		for (i=0;i<n_orb_frames;i++) {
			Vclip[orb_vclip].frames[i].index = bitmap_num;
			init_bitmap(&GameBitmaps[bitmap_num],orb_w,orb_h,BM_FLAG_TRANSPARENT,bitmap_data);
			bitmap_data += orb_w*orb_h;
			bitmap_num++;
			Assert(bitmap_num < MAX_BITMAP_FILES);
		}

		//Create obj powerup
		Powerup_info[POW_HOARD_ORB].vclip_num = orb_vclip;
		Powerup_info[POW_HOARD_ORB].hit_sound = -1;	//Powerup_info[POW_SHIELD_BOOST].hit_sound;
		Powerup_info[POW_HOARD_ORB].size = Powerup_info[POW_SHIELD_BOOST].size;
		Powerup_info[POW_HOARD_ORB].light = Powerup_info[POW_SHIELD_BOOST].light;

		//Create orb goal wall effect
		Hoard_goal_eclip = Num_effects++;
		Assert(Num_effects < MAX_EFFECTS);
		Effects[Hoard_goal_eclip] = Effects[94];		//copy from blue goal
		Effects[Hoard_goal_eclip].changing_wall_texture = NumTextures;
		Effects[Hoard_goal_eclip].vc.num_frames=n_goal_frames;
	
		TmapInfo[NumTextures] = TmapInfo[find_goal_texture(TMI_GOAL_BLUE)];
		TmapInfo[NumTextures].eclip_num = Hoard_goal_eclip;
		TmapInfo[NumTextures].flags = TMI_GOAL_HOARD;
		NumTextures++;
		Assert(NumTextures < MAX_TEXTURES);
		for (i=0;i<n_goal_frames;i++) {
			Effects[Hoard_goal_eclip].vc.frames[i].index = bitmap_num;
			init_bitmap(&GameBitmaps[bitmap_num],64,64,0,bitmap_data);
			bitmap_data += 64*64;
			bitmap_num++;
			Assert(bitmap_num < MAX_BITMAP_FILES);
		}

	}

	//Load and remap bitmap data for orb
	cfread(palette,3,256,ifile);
	for (i=0;i<n_orb_frames;i++) {
		grs_bitmap *bm = &GameBitmaps[Vclip[orb_vclip].frames[i].index];
		cfread(bm->bm_data,1,orb_w*orb_h,ifile);
		gr_remap_bitmap_good( bm, palette, 255, -1 );
	}

	//Load and remap bitmap data for goal texture
	cfile_read_short(ifile);		//skip frame count
	cfread(palette,3,256,ifile);
	for (i=0;i<n_goal_frames;i++) {
		grs_bitmap *bm = &GameBitmaps[Effects[Hoard_goal_eclip].vc.frames[i].index];
		cfread(bm->bm_data,1,64*64,ifile);
		gr_remap_bitmap_good( bm, palette, 255, -1 );
	}

	//Load and remap bitmap data for HUD icons
	for (i=0;i<2;i++) {
		icon_w = cfile_read_short(ifile);
		icon_h = cfile_read_short(ifile);
		if (first_time) {
			ubyte *bitmap_data;
			MALLOC( bitmap_data, ubyte, icon_w*icon_h );
			init_bitmap(&Orb_icons[i],icon_w,icon_h,BM_FLAG_TRANSPARENT,bitmap_data);
		}
		cfread(palette,3,256,ifile);
		cfread(Orb_icons[i].bm_data,1,icon_w*icon_h,ifile);
		gr_remap_bitmap_good( &Orb_icons[i], palette, 255, -1 );
	}

	if (first_time) {

		//Load sounds for orb game

		for (i=0;i<4;i++) {
			int len;

			len = cfile_read_int(ifile);	//get 11k len

			if (digi_sample_rate == SAMPLE_RATE_22K) {
				cfseek(ifile,len,SEEK_CUR);		//skip over 11k sample
				len = cfile_read_int(ifile);		//get 22k len
			}

			GameSounds[Num_sound_files+i].length = len;
			GameSounds[Num_sound_files+i].data = malloc(len);
			cfread(GameSounds[Num_sound_files+i].data,1,len,ifile);

			if (digi_sample_rate == SAMPLE_RATE_11K) {
				len = cfile_read_int(ifile);		//get 22k len
				cfseek(ifile,len,SEEK_CUR);		//skip over 22k sample
			}

			Sounds[SOUND_YOU_GOT_ORB+i] = Num_sound_files+i;
			AltSounds[SOUND_YOU_GOT_ORB+i] = Sounds[SOUND_YOU_GOT_ORB+i];
		}
	}

	cfclose(ifile);

	first_time = 0;
}

#endif

