/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "network/multiteamselect.h" #include "gamesequence/gamesequence.h" #include "gamesnd/gamesnd.h" #include "globalincs/alphacolors.h" #include "globalincs/linklist.h" #include "graphics/font.h" #include "io/key.h" #include "io/mouse.h" #include "menuui/snazzyui.h" #include "mission/missionparse.h" #include "missionui/chatbox.h" #include "missionui/missionscreencommon.h" #include "missionui/missionshipchoice.h" #include "missionui/missionweaponchoice.h" #include "missionui/missionbrief.h" #include "network/multi.h" #include "network/multi_endgame.h" #include "network/multimsgs.h" #include "network/multiui.h" #include "network/multiutil.h" #include "object/object.h" #include "parse/parselo.h" #include "playerman/player.h" #include "popup/popup.h" #include "ship/ship.h" #include "utils/string_utils.h" #include "weapon/weapon.h" // ------------------------------------------------------------------------------------------------------ // TEAM SELECT DEFINES/VARS // // mission screen common data extern int Next_screen; //XSTR:OFF // bitmap defines #define MULTI_TS_PALETTE "InterfacePalette" const char *Multi_ts_bitmap_fname[GR_NUM_RESOLUTIONS] = { "TeamSelect", // GR_640 "2_TeamSelect" // GR_1024 }; const char *Multi_ts_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { "TeamSelect-M", // GR_640 "2_TeamSelect-M" // GR_1024 }; // constants for coordinate lookup #define MULTI_TS_X_COORD 0 #define MULTI_TS_Y_COORD 1 #define MULTI_TS_W_COORD 2 #define MULTI_TS_H_COORD 3 #define MULTI_TS_NUM_BUTTONS 7 #define MULTI_TS_BRIEFING 0 // go to the briefing #define MULTI_TS_SHIP_SELECT 1 // this screen #define MULTI_TS_WEAPON_SELECT 2 // go to the weapon select screen #define MULTI_TS_SHIPS_UP 3 // scroll the ships list up #define MULTI_TS_SHIPS_DOWN 4 // scroll the ships list down #define MULTI_TS_COMMIT 5 // commit #define MULTI_TS_LOCK 6 // lock (free) ship/weapon select ui_button_info Multi_ts_buttons[GR_NUM_RESOLUTIONS][MULTI_TS_NUM_BUTTONS] = { { // GR_640 ui_button_info("CB_00", 7, 3, 37, 7, 0), ui_button_info("CB_01", 7, 19, 37, 23, 1), ui_button_info("CB_02", 7, 35, 37, 39, 2), ui_button_info("TSB_03", 5, 303, -1, -1, 3), ui_button_info("TSB_04", 5, 454, -1, -1, 4), ui_button_info("TSB_09", 571, 425, 572, 413, 9), ui_button_info("TSB_34", 603, 374, 602, 364, 34) }, { // GR_1024 ui_button_info("2_CB_00", 12, 5, 59, 12, 0), ui_button_info("2_CB_01", 12, 31, 59, 37, 1), ui_button_info("2_CB_02", 12, 56, 59, 62, 2), ui_button_info("2_TSB_03", 8, 485, -1, -1, 3), ui_button_info("2_TSB_04", 8, 727, -1, -1, 4), ui_button_info("2_TSB_09", 914, 681, 937, 660, 9), ui_button_info("2_TSB_34", 966, 599, 964, 584, 34) }, }; // players locked ani graphic #define MULTI_TS_NUM_LOCKED_BITMAPS 3 const char *Multi_ts_bmap_names[GR_NUM_RESOLUTIONS][3] = { { // GR_640 "TSB_340000", "TSB_340001", "TSB_340002" }, { // GR_1024 "2_TSB_340000", "2_TSB_340001", "2_TSB_340002" } }; int Multi_ts_locked_bitmaps[MULTI_TS_NUM_LOCKED_BITMAPS]; // snazzy menu regions #define TSWING_0_SHIP_0 10 #define TSWING_0_SHIP_1 12 #define TSWING_0_SHIP_2 14 #define TSWING_0_SHIP_3 16 #define TSWING_1_SHIP_0 18 #define TSWING_1_SHIP_1 20 #define TSWING_1_SHIP_2 22 #define TSWING_1_SHIP_3 24 #define TSWING_2_SHIP_0 26 #define TSWING_2_SHIP_1 28 #define TSWING_2_SHIP_2 30 #define TSWING_2_SHIP_3 32 #define TSWING_0_NAME_0 11 #define TSWING_0_NAME_1 13 #define TSWING_0_NAME_2 15 #define TSWING_0_NAME_3 17 #define TSWING_1_NAME_0 19 #define TSWING_1_NAME_1 21 #define TSWING_1_NAME_2 23 #define TSWING_1_NAME_3 25 #define TSWING_2_NAME_0 27 #define TSWING_2_NAME_1 29 #define TSWING_2_NAME_2 31 #define TSWING_2_NAME_3 33 #define TSWING_LIST_0 5 #define TSWING_LIST_1 6 #define TSWING_LIST_2 7 #define TSWING_LIST_3 8 #define MULTI_TS_SLOT_LIST 0 #define MULTI_TS_PLAYER_LIST 1 #define MULTI_TS_AVAIL_LIST 2 // interface data #define MULTI_TS_NUM_SNAZZY_REGIONS 28 int Multi_ts_bitmap; int Multi_ts_mask; int Multi_ts_inited = 0; int Multi_ts_snazzy_regions; ubyte *Multi_ts_mask_data; int Multi_ts_mask_w, Multi_ts_mask_h; std::array Multi_ts_region; UI_WINDOW Multi_ts_window; // ship slot data #define MULTI_TS_NUM_SHIP_SLOTS_TEAM 4 // # of ship slots in team v team #define MULTI_TS_FLAG_NONE -2 // never has any ships #define MULTI_TS_FLAG_EMPTY -1 // currently empty static int Multi_ts_slot_icon_coords[MULTI_TS_NUM_SHIP_SLOTS][GR_NUM_RESOLUTIONS][2] = { // x,y { // {128,301}, // GR_640 {205,482} // GR_1024 }, { // {91,347}, // GR_640 {146,555} // GR_1024 }, { // {166,347}, // GR_640 {266,555} // GR_1024 }, { // alpha {128,395}, // GR_640 {205,632} // GR_1024 }, { // {290,301}, // GR_640 {464,482} // GR_1024 }, { // {253,347}, // GR_640 {405,555} // GR_1024 }, { // {328,347}, // GR_640 {525,555} // GR_1024 }, { // beta {290,395}, // GR_640 {464,632} // GR_1024 }, { // {453,301}, // GR_640 {725,482} // GR_1024 }, { // {416,347}, // GR_640 {666,555} // GR_1024 }, { // {491,347}, // GR_640 {786,555} // GR_1024 }, { // gamma {453,395}, // GR_640 {725,632} // GR_1024 } }; static int Multi_ts_slot_text_coords[MULTI_TS_NUM_SHIP_SLOTS][GR_NUM_RESOLUTIONS][3] = { // x,y,width { // alpha {112,330,181-112}, // GR_640 {187,517,181-112} // GR_1024 }, { // alpha {74,377,143-74}, // GR_640 {126,592,143-74} // GR_1024 }, { // alpha {149,377,218-149},// GR_640 {248,592,218-149} // GR_1024 }, { // alpha {112,424,181-112},// GR_640 {187,667,181-112} // GR_1024 }, { // beta {274,330,343-274},// GR_640 {446,517,343-274} // GR_1024 }, { // beta {236,377,305-236},// GR_640 {385,592,305-236} // GR_1024 }, { // beta {311,377,380-311},// GR_640 {507,592,380-311} // GR_1024 }, { // beta {274,424,343-274},// GR_640 {446,667,343-274} // GR_1024 }, { // gamma {437,330,506-437},// GR_640 {707,517,506-437} // GR_1024 }, { // gamma {399,377,468-399},// GR_640 {646,592,468-399} // GR_1024 }, { // gamma {474,377,543-474},// GR_640 {768,592,543-474} // GR_1024 }, { // gamma {437,424,506-437},// GR_640 {707,667,506-437} // GR_1024 } }; // avail ship list data #define MULTI_TS_AVAIL_MAX_DISPLAY 4 static int Multi_ts_avail_coords[MULTI_TS_AVAIL_MAX_DISPLAY][GR_NUM_RESOLUTIONS][2] = { // x,y coords { // {23,331}, // GR_640 {37,530} // GR_1024 }, { // {23,361}, // GR_640 {37,578} // GR_1024 }, { // {23,391}, // GR_640 {37,626} // GR_1024 }, { // {23,421}, // GR_640 {37,674} // GR_1024 } }; int Multi_ts_avail_start = 0; // starting index of where we will display the available ships int Multi_ts_avail_count = 0; // the # of available ship classes // ship information stuff #define MULTI_TS_SHIP_INFO_MAX_LINE_LEN 150 #define MULTI_TS_SHIP_INFO_MAX_LINES 10 #define MULTI_TS_SHIP_INFO_MAX_TEXT (MULTI_TS_SHIP_INFO_MAX_LINE_LEN * MULTI_TS_SHIP_INFO_MAX_LINES) static int Multi_ts_ship_info_coords[GR_NUM_RESOLUTIONS][3] = { { // GR_640 33, 150, 387 }, { // GR_1024 53, 240, 618 } }; char Multi_ts_ship_info_lines[MULTI_TS_SHIP_INFO_MAX_LINES][MULTI_TS_SHIP_INFO_MAX_LINE_LEN]; char Multi_ts_ship_info_text[MULTI_TS_SHIP_INFO_MAX_TEXT]; int Multi_ts_ship_info_line_count; // status bar mode static int Multi_ts_status_coords[GR_NUM_RESOLUTIONS][3] = { { // GR_640 95, 467, 426 }, { // GR_1024 152, 747, 688 } }; int Multi_ts_status_bar_mode = 0; // carried icon information int Multi_ts_carried_flag = 0; int Multi_ts_clicked_flag = 0; int Multi_ts_clicked_x,Multi_ts_clicked_y; int Multi_ts_carried_ship_class; int Multi_ts_carried_from_type = 0; int Multi_ts_carried_from_index = 0; // selected ship types (for informational purposes) int Multi_ts_select_type = -1; int Multi_ts_select_index = -1; int Multi_ts_select_ship_class = -1; // per-frame mouse hotspot vars int Multi_ts_hotspot_type = -1; int Multi_ts_hotspot_index = -1; // operation types #define TS_GRAB_FROM_LIST 0 #define TS_SWAP_LIST_SLOT 1 #define TS_SWAP_SLOT_SLOT 2 #define TS_DUMP_TO_LIST 3 #define TS_SWAP_PLAYER_PLAYER 4 #define TS_MOVE_PLAYER 5 // team data #define MULTI_TS_FLAG_NONE -2 // slot is _always_ empty #define MULTI_TS_FLAG_EMPTY -1 // flag is temporarily empty typedef struct ts_team_data { int multi_ts_objnum[MULTI_TS_NUM_SHIP_SLOTS]; // objnums for all slots in this team net_player *multi_ts_player[MULTI_TS_NUM_SHIP_SLOTS]; // net players corresponding to the same slots int multi_ts_flag[MULTI_TS_NUM_SHIP_SLOTS]; // flags indicating the "status" of a slot int multi_players_locked; // are the players locked into place } ts_team_data; ts_team_data Multi_ts_team[MULTI_TS_MAX_TVT_TEAMS]; // data for all teams // deleted ship objnums int Multi_ts_deleted_objnums[MULTI_TS_MAX_TVT_TEAMS * MULTI_TS_NUM_SHIP_SLOTS]; int Multi_ts_num_deleted; //XSTR:ON // ------------------------------------------------------------------------------------------------------ // TEAM SELECT FORWARD DECLARATIONS // // check for button presses void multi_ts_check_buttons(); // act on a button press void multi_ts_button_pressed(int n); // initialize all screen data, etc void multi_ts_init_graphics(); // blit all of the icons representing all wings void multi_ts_blit_wings(); // blit all of the player callsigns under the correct ships void multi_ts_blit_wing_callsigns(); // blit the ships on the avail list void multi_ts_blit_avail_ships(); // initialize the snazzy menu stuff for dragging ships,players around void multi_ts_init_snazzy(); // what type of region the index is (0 == ship avail list, 1 == ship slots, 2 == player slot) int multi_ts_region_type(int region); // convert the region num to a ship slot index int multi_ts_slot_index(int region); // convert the region num to an avail list index int multi_ts_avail_index(int region); // convert the region num to a player slot index int multi_ts_player_index(int region); // blit the status bar void multi_ts_blit_status_bar(); // assign the correct players to the correct slots void multi_ts_init_players(); // assign the correct objnums to the correct slots void multi_ts_init_objnums(); // assign the correct flags to the correct slots void multi_ts_init_flags(); // handle an available ship scroll down button press void multi_ts_avail_scroll_down(); // handle an available ship scroll up button press void multi_ts_avail_scroll_up(); // handle all mouse events (clicking, dragging, and dropping) void multi_ts_handle_mouse(); // can the specified player perform the action he is attempting int multi_ts_can_perform(int from_type,int from_index,int to_type,int to_index,int ship_class,int player_index = -1); // determine the kind of drag and drop operation this is int multi_ts_get_dnd_type(int from_type,int from_index,int to_type,int to_index,int player_index = -1); // swap two player positions int multi_ts_swap_player_player(int from_index,int to_index,interface_snd_id *sound,int player_index = -1); // move a player int multi_ts_move_player(int from_index,int to_index,interface_snd_id *sound,int player_index = -1); // get the ship class of the current index in the avail list or -1 if none exists int multi_ts_get_avail_ship_class(int index); // blit the currently carried icon (if any) void multi_ts_blit_carried_icon(); // if the (console) player is allowed to grab a player slot at this point int multi_ts_can_grab_player(int slot_index,int player_index = -1); // return the bitmap index into the ships icon array (in ship select) which should be displayed for the given slot int multi_ts_slot_bmap_num(int slot_index); // blit any active ship information text void multi_ts_blit_ship_info(); // select the given slot and setup any information, etc void multi_ts_select_ship(); // is it ok for this player to commit int multi_ts_ok_to_commit(); // return the bitmap index into the ships icon array (in ship select) which should be displayed for the given slot int multi_ts_avail_bmap_num(int slot_index); // set the status bar to reflect the status of wing slots (free or not free). 0 or 1 are valid values for now void multi_ts_set_status_bar_mode(int m); // check to see that no illegal ship settings have occurred void multi_ts_check_errors(); // ------------------------------------------------------------------------------------------------------ // TEAM SELECT FUNCTIONS // // initialize the team select screen (always call, even when switching between weapon select, etc) void multi_ts_init() { // if we haven't initialized at all yet, then do it if(!Multi_ts_inited){ multi_ts_init_graphics(); Multi_ts_inited = 1; } // use the common interface palette multi_common_set_palette(); Net_player->state = NETPLAYER_STATE_SHIP_SELECT; Current_screen = ON_SHIP_SELECT; } // initialize all critical internal data structures void multi_ts_common_init() { int idx; // reset timestamps here. they seem to get hosed by the loadinh of the mission file multi_reset_timestamps(); // saying "not allowed to mess with ships" Multi_ts_status_bar_mode = 0; // intialize ship info stuff memset(Multi_ts_ship_info_text,0,MULTI_TS_SHIP_INFO_MAX_TEXT); memset(Multi_ts_ship_info_lines,0,MULTI_TS_SHIP_INFO_MAX_TEXT); Multi_ts_ship_info_line_count = 0; // initialize carried icon information Multi_ts_carried_flag = 0; Multi_ts_clicked_flag = 0; Multi_ts_clicked_x = 0; Multi_ts_clicked_y = 0; Multi_ts_carried_ship_class = -1; Multi_ts_carried_from_type = 0; Multi_ts_carried_from_index = 0; // selected slot information (should be default player ship) if(!MULTI_PERM_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){ Multi_ts_select_type = MULTI_TS_SLOT_LIST; Multi_ts_select_index = Net_player->p_info.ship_index; // select this ship and setup his info Multi_ts_select_ship_class = Wss_slots[Multi_ts_select_index].ship_class; multi_ts_select_ship(); } else { Multi_ts_select_type = -1; Multi_ts_select_index = -1; // no ship class selected for information purposes Multi_ts_select_ship_class = -1; } // deleted ship information memset(Multi_ts_deleted_objnums,0,sizeof(int) * MULTI_TS_MAX_TVT_TEAMS * MULTI_TS_NUM_SHIP_SLOTS); Multi_ts_num_deleted = 0; // mouse hotspot information Multi_ts_hotspot_type = -1; Multi_ts_hotspot_index = -1; // initialize avail ship list data Multi_ts_avail_start = 0; // load the locked button bitmaps bitmaps for(idx=0;idxflags & NETINFO_FLAG_TEAM_CAPTAIN)) || ((Netgame.type_flags & NG_TYPE_TEAM) && !(Net_player->flags & NETINFO_FLAG_GAME_HOST)) ){ Multi_ts_buttons[gr_screen.res][MULTI_TS_LOCK].button.draw_forced(0); } else { Multi_ts_buttons[gr_screen.res][MULTI_TS_LOCK].button.draw(); } } // blit any active ship information multi_ts_blit_ship_info(); // blit the status bar multi_ts_blit_status_bar(); // render the chatbox chatbox_render(); // render tooltips Multi_ts_window.draw_tooltip(); // display the status of the voice system multi_common_voice_display_status(); // blit any carried icons multi_ts_blit_carried_icon(); // flip the buffer gr_flip(); // If the commit button was pressed, do the commit button actions. Done at the end of the // loop since we are a bit special and have to close out in the proper order. if ( Commit_pressed ) { multi_ts_commit_pressed(); Commit_pressed = 0; } } // close the team select screen (always call, even when switching between weapon select, etc) void multi_ts_close() { int idx; if(!Multi_ts_inited){ return; } Multi_ts_inited = 0; // shut down the snazzy menu snazzy_menu_close(); // unload any bitmaps if(!bm_unload(Multi_ts_bitmap)){ nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ts_bitmap_fname[gr_screen.res])); } for(idx=0;idxflags & NETINFO_FLAG_OBSERVER){ return 1; } // if the flag for this team isn't set to "free" we can't do anything if(!Multi_ts_team[pl->p_info.team].multi_players_locked){ return 1; } // if the "leaders" only flag is set if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){ // in a team vs. team situation if(Netgame.type_flags & NG_TYPE_TEAM){ if(pl->flags & NETINFO_FLAG_TEAM_CAPTAIN){ return 0; } } // in a non team vs. team situation else { if(pl->flags & NETINFO_FLAG_GAME_HOST){ return 0; } } } else { // in a team vs. team situation if(Netgame.type_flags & NG_TYPE_TEAM){ // if i'm the team captain I can mess with my own ships as well as those of the ai ships on my team if(pl->flags & NETINFO_FLAG_TEAM_CAPTAIN){ if((Multi_ts_team[pl->p_info.team].multi_ts_player[slot_num] != NULL) && (Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[slot_num]].flags[Object::Object_Flags::Player_ship]) && (slot_num != pl->p_info.ship_index)){ return 1; } return 0; } } // in a non team vs. team situation else { // if we're the host, we can our own ship and ai ships if(pl->flags & NETINFO_FLAG_GAME_HOST){ // can't grab player ships if((Multi_ts_team[pl->p_info.team].multi_ts_player[slot_num] != NULL) && (Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[slot_num]].flags[Object::Object_Flags::Player_ship]) && (slot_num != pl->p_info.ship_index)){ return 1; } return 0; } } // if this is our slot, then we can grab it if(slot_num == pl->p_info.ship_index){ return 0; } } return 1; } // is the given slot disabled for the specified player, _and_ it is his ship as well int multi_ts_disabled_high_slot(int slot_index,int player_index) { net_player *pl; // get the appropriate net player if(player_index == -1){ pl = Net_player; } else { pl = &Net_players[player_index]; } // if this is disabled for him and its also _his_ slot if(multi_ts_disabled_slot(slot_index,player_index) && !Multi_ts_team[pl->p_info.team].multi_players_locked && (slot_index == pl->p_info.ship_index)){ return 1; } return 0; } // resynch all display/interface elements based upon all the ship/weapon pool values void multi_ts_sync_interface() { int idx; // item 1 - determine how many ship types are available in the ship pool Multi_ts_avail_count = 0; for(const auto &[ship_class, count] : *Ss_pool) { if(count > 0){ Multi_ts_avail_count++; } } // item 2 - make sure our local Multi_ts_slot_flag array is up to date multi_ts_init_flags(); // item 3 - set/unset any necessary flags in underlying ship select data structures for(idx=0;idxp_info.team].multi_ts_flag[idx]){ case MULTI_TS_FLAG_EMPTY : ss_make_slot_empty(idx); break; case MULTI_TS_FLAG_NONE : break; default : ss_make_slot_full(idx); break; } } // item 4 - reset the locked/unlocked status of all ships in the weapon select screen ss_recalc_multiplayer_slots(); } void multi_ts_assign_players_all() { int idx,team_index,slot_index,found,player_count,shipnum; char name_lookup[NAME_LENGTH]; object *objp; // set all player ship indices to -1 for(idx=0;idxp_info.team], 1); } else { // To account for cases where 1 is not a player ship for (int i = 0; i < MAX_SHIPS_PER_WING; i++) { wing_bash_ship_name(name_lookup, TVT_wing_names[0], i + 1); if (!stricmp(name_lookup, Player_start_shipname)) break; } } shipnum = ship_name_lookup(name_lookup); // if we couldn't find the ship for the host if(shipnum == -1){ // Netgame.flags |= NG_FLAG_QUITTING; multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_SHIP_ASSIGN); return; } multi_ts_get_team_and_slot(Ships[shipnum].ship_name,&team_index,&slot_index); multi_assign_player_ship(NET_PLAYER_INDEX(Netgame.host),&Objects[Ships[shipnum].objnum],Ships[shipnum].ship_info_index); Netgame.host->p_info.ship_index = slot_index; Assert(Netgame.host->p_info.ship_index >= 0); Netgame.host->p_info.ship_class = Ships[shipnum].ship_info_index; Netgame.host->m_player->objnum = Ships[shipnum].objnum; // for each netplayer, try and find a ship objp = GET_FIRST(&obj_used_list); while(objp != END_OF_LIST(&obj_used_list)){ // find a valid player ship - ignoring the ship which was assigned to the host if((objp->flags[Object::Object_Flags::Player_ship]) && stricmp(Ships[objp->instance].ship_name,name_lookup) != 0){ // determine what team and slot this ship is multi_ts_get_team_and_slot(Ships[objp->instance].ship_name,&team_index,&slot_index); Assert((team_index != -1) && (slot_index != -1)); // in a team vs. team situation if(Netgame.type_flags & NG_TYPE_TEAM){ // find a player on this team who needs a ship found = 0; for(idx=0;idxinstance].ship_info_index); Net_players[idx].p_info.ship_index = slot_index; Assert(Net_players[idx].p_info.ship_index >= 0); Net_players[idx].p_info.ship_class = Ships[objp->instance].ship_info_index; Net_players[idx].m_player->objnum = OBJ_INDEX(objp); // decrement the player count player_count--; } else { objp->flags.remove(Object::Object_Flags::Player_ship); obj_set_flags( objp, objp->flags + Object::Object_Flags::Could_be_player); } // if we've assigned all players, we're done if(player_count <= 0){ break; } } // move to the next item objp = GET_NEXT(objp); } // go through and change any ships marked as player ships to be COULD_BE_PLAYER if ( objp != END_OF_LIST(&obj_used_list) ) { for ( objp = GET_NEXT(objp); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { if ( objp->flags[Object::Object_Flags::Player_ship] ){ objp->flags.remove(Object::Object_Flags::Player_ship); obj_set_flags( objp, objp->flags + Object::Object_Flags::Could_be_player); } } } if(Game_mode & GM_STANDALONE_SERVER){ Player_obj = NULL; //Net_player->m_player->objnum = -1; } // check to make sure all players were assigned correctly for(idx=0;idx= 0){ int objnum; // set the ship type appropriately Assert(Wss_slots_teams[idx][s_idx].ship_class >= 0); objnum = Multi_ts_team[idx].multi_ts_objnum[s_idx]; change_ship_type(Objects[objnum].instance,Wss_slots_teams[idx][s_idx].ship_class); // set the ship weapons correctly wl_update_ship_weapons(objnum,&Wss_slots_teams[idx][s_idx]); // assign ts_index of the ship to point to the proper Wss_slots slot Ships[Objects[objnum].instance].ts_index = s_idx; } else if(Multi_ts_team[idx].multi_ts_flag[s_idx] == MULTI_TS_FLAG_EMPTY){ Assert(Multi_ts_team[idx].multi_ts_objnum[s_idx] >= 0); // mark the object as having been deleted Multi_ts_deleted_objnums[Multi_ts_num_deleted] = Multi_ts_team[idx].multi_ts_objnum[s_idx]; // delete the ship ship_add_exited_ship( &Ships[Objects[Multi_ts_deleted_objnums[Multi_ts_num_deleted]].instance], Ship::Exit_Flags::Player_deleted); obj_delete(Multi_ts_deleted_objnums[Multi_ts_num_deleted]); ship_wing_cleanup(Objects[Multi_ts_deleted_objnums[Multi_ts_num_deleted]].instance,&Wings[Ships[Objects[Multi_ts_team[idx].multi_ts_objnum[s_idx]].instance].wingnum]); // increment the # of ships deleted Multi_ts_num_deleted++; } } } } // do any necessary processing for players who have left the game void multi_ts_handle_player_drop() { int idx,s_idx; // find the player for(idx=0;idxflags & NETINFO_FLAG_TEAM_CAPTAIN)) { return; } } else { if (!(Net_player->flags & NETINFO_FLAG_GAME_HOST)) { return; } } gamesnd_play_iface(InterfaceSounds::USER_SELECT); // send a final player slot update packet send_pslot_update_packet(Net_player->p_info.team,TS_CODE_LOCK_TEAM, interface_snd_id()); Multi_ts_team[Net_player->p_info.team].multi_players_locked = 1; // sync interface stuff multi_ts_set_status_bar_mode(1); multi_ts_sync_interface(); ss_recalc_multiplayer_slots(); // disable this button now Multi_ts_buttons[gr_screen.res][MULTI_TS_LOCK].button.disable(); } // if i'm "locked" int multi_ts_is_locked() { return Multi_ts_team[Net_player->p_info.team].multi_players_locked; } // show a popup saying "only host and team captains can modify, etc, etc" void multi_ts_maybe_host_only_popup() { /* // if this is because the "host modifies" option is set if((Netgame.options.flags & MSO_FLAG_SS_LEADERS) && !(Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN)){ multi_ts_host_only_popup(); } if(Netgame.type == NG_TYPE_TEAM){ popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,"Only team captains may modify ships and weapons in this game"); } else { popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,"Only the host may modify ships and weapons in this game"); } */ } // ------------------------------------------------------------------------------------------------------ // TEAM SELECT FORWARD DEFINITIONS // // check for button presses void multi_ts_check_buttons() { int idx; for(idx=0;idxship_select_background[gr_screen.res], Multi_ts_bitmap_fname[gr_screen.res]); if(Multi_ts_bitmap < 0){ // we failed to load the bitmap - this is very bad Int3(); } // create the interface buttons for(idx=0;idxflags & NETINFO_FLAG_TEAM_CAPTAIN)){ Multi_ts_buttons[gr_screen.res][MULTI_TS_LOCK].button.disable(); } } else { if(!(Net_player->flags & NETINFO_FLAG_GAME_HOST)){ Multi_ts_buttons[gr_screen.res][MULTI_TS_LOCK].button.disable(); } } // initialize the snazzy menu stuff (for grabbing ships, names, etc) multi_ts_init_snazzy(); // create the chatbox (again, should not be necessary at this point) chatbox_create(); // sync the interface as normal multi_ts_sync_interface(); } // blit all of the icons representing all wings void multi_ts_blit_wings() { int idx; // blit them all blindly for now for(idx=0;idxp_info.team].multi_ts_flag[idx] == MULTI_TS_FLAG_NONE){ continue; } // otherwise blit the ship icon or the "empty" icon if(Multi_ts_team[Net_player->p_info.team].multi_ts_flag[idx] == MULTI_TS_FLAG_EMPTY){ ss_blit_ship_icon(Multi_ts_slot_icon_coords[idx][gr_screen.res][MULTI_TS_X_COORD],Multi_ts_slot_icon_coords[idx][gr_screen.res][MULTI_TS_Y_COORD],-1,0); } else { ss_blit_ship_icon(Multi_ts_slot_icon_coords[idx][gr_screen.res][MULTI_TS_X_COORD],Multi_ts_slot_icon_coords[idx][gr_screen.res][MULTI_TS_Y_COORD],Wss_slots[idx].ship_class,multi_ts_slot_bmap_num(idx)); // if this is a team vs team game, and the slot is occupised by a team captain, put a c there if((Netgame.type_flags & NG_TYPE_TEAM) && (Multi_ts_team[Net_player->p_info.team].multi_ts_player[idx] != NULL) && (Multi_ts_team[Net_player->p_info.team].multi_ts_player[idx]->flags & NETINFO_FLAG_TEAM_CAPTAIN)){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_slot_icon_coords[idx][gr_screen.res][MULTI_TS_X_COORD] - 5,Multi_ts_slot_icon_coords[idx][gr_screen.res][MULTI_TS_Y_COORD] - 5, XSTR("C",737), GR_RESIZE_MENU); // [[ Team captain ]] } } } } bool multi_ts_is_player_in_slot(int slot_idx) { Assertion(slot_idx >= 0 && slot_idx < MAX_WSS_SLOTS, "Slot index is out of bounds!"); Assertion(Net_player != nullptr, "Cannot check the slot for the player because the player is null!"); return (Multi_ts_team[Net_player->p_info.team].multi_ts_player[slot_idx] != nullptr); } bool multi_ts_is_player_allowed_in_slot(int slot_idx) { Assertion(slot_idx >= 0 && slot_idx < MAX_WSS_SLOTS, "Slot index is out of bounds!"); Assertion(Net_player != nullptr, "Cannot check the slot for the player because the player is null!"); p_object * pobj = mission_parse_get_arrival_ship(Ships[Objects[Multi_ts_team[Net_player->p_info.team].multi_ts_objnum[slot_idx]].instance].ship_name); if ((pobj == NULL) || !(pobj->flags[Mission::Parse_Object_Flags::OF_Player_start])) { return false; } return true; } // blit all of the player callsigns under the correct ships void multi_ts_blit_wing_callsigns() { int idx,callsign_w; char callsign[CALLSIGN_LEN+2]; // blit them all blindly for now for(idx=0;idxp_info.team].multi_ts_flag[idx] == MULTI_TS_FLAG_NONE){ continue; } // if there is a player in the slot if (multi_ts_is_player_in_slot(idx)) { // make sure the string fits strcpy_s(callsign,Multi_ts_team[Net_player->p_info.team].multi_ts_player[idx]->m_player->callsign); } else { // determine if this is a locked AI ship if (!multi_ts_is_player_allowed_in_slot(idx)) { strcpy_s(callsign, NOX("<")); strcat_s(callsign, XSTR("AI",738)); // [[ Artificial Intellegence ]] strcat_s(callsign, NOX(">")); } else { strcpy_s(callsign, XSTR("AI",738)); // [[ Artificial Intellegence ]] } } font::force_fit_string(callsign, CALLSIGN_LEN, Multi_ts_slot_text_coords[idx][gr_screen.res][MULTI_TS_W_COORD]); // get the final length gr_get_string_size(&callsign_w, NULL, callsign); // blit the string if((Multi_ts_hotspot_type == MULTI_TS_PLAYER_LIST) && (Multi_ts_hotspot_index == idx) && (Multi_ts_team[Net_player->p_info.team].multi_ts_player[idx] != NULL)){ gr_set_color_fast(&Color_text_active_hi); } else { gr_set_color_fast(&Color_normal); } gr_string(Multi_ts_slot_text_coords[idx][gr_screen.res][MULTI_TS_X_COORD] + ((Multi_ts_slot_text_coords[idx][gr_screen.res][MULTI_TS_W_COORD] - callsign_w)/2),Multi_ts_slot_text_coords[idx][gr_screen.res][MULTI_TS_Y_COORD],callsign,GR_RESIZE_MENU); } } // blit the ships on the avail list void multi_ts_blit_avail_ships() { int display_count,ship_count; char count[6]; // blit the availability of all ship counts display_count = 0; ship_count = 0; for(const auto &[ship_class, pool_count] : *Ss_pool) { if(pool_count > 0){ // if our starting display index is after this, then skip it if(ship_count < Multi_ts_avail_start){ ship_count++; } else { // blit the icon ss_blit_ship_icon(Multi_ts_avail_coords[display_count][gr_screen.res][MULTI_TS_X_COORD],Multi_ts_avail_coords[display_count][gr_screen.res][MULTI_TS_Y_COORD],ship_class,multi_ts_avail_bmap_num(display_count)); // blit the ship count available sprintf(count,"%d",pool_count); gr_set_color_fast(&Color_normal); gr_string(Multi_ts_avail_coords[display_count][gr_screen.res][MULTI_TS_X_COORD] - 20,Multi_ts_avail_coords[display_count][gr_screen.res][MULTI_TS_Y_COORD],count,GR_RESIZE_MENU); // increment the counts display_count++; ship_count++; } } // if we've reached the max amount we can display, then stop if(display_count >= MULTI_TS_AVAIL_MAX_DISPLAY){ return; } } } // initialize the snazzy menu stuff for dragging ships,players around void multi_ts_init_snazzy() { // initialize the snazzy menu snazzy_menu_init(); // blast the data Multi_ts_snazzy_regions = 0; Multi_ts_region.fill({}); // get a pointer to the mask bitmap data Multi_ts_mask_data = Multi_ts_window.get_mask_data(&Multi_ts_mask_w, &Multi_ts_mask_h); // add the wing slots information snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_SHIP_0, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_SHIP_1, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_SHIP_2, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_SHIP_3, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_SHIP_0, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_SHIP_1, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_SHIP_2, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_SHIP_3, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_SHIP_0, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_SHIP_1, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_SHIP_2, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_SHIP_3, 0); // add the name slots information snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_NAME_0, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_NAME_1, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_NAME_2, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_0_NAME_3, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_NAME_0, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_NAME_1, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_NAME_2, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_1_NAME_3, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_NAME_0, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_NAME_1, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_NAME_2, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_2_NAME_3, 0); // add the available ships region snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_LIST_0, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_LIST_1, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_LIST_2, 0); snazzy_menu_add_region(&Multi_ts_region[Multi_ts_snazzy_regions++], "", TSWING_LIST_3, 0); } // what type of region the index is (0 == ship avail list, 1 == ship slots, 2 == player slot) int multi_ts_region_type(int region) { if((region == TSWING_0_SHIP_0) || (region == TSWING_0_SHIP_1) || (region == TSWING_0_SHIP_2) || (region == TSWING_0_SHIP_3) || (region == TSWING_1_SHIP_0) || (region == TSWING_1_SHIP_1) || (region == TSWING_1_SHIP_2) || (region == TSWING_1_SHIP_3) || (region == TSWING_2_SHIP_0) || (region == TSWING_2_SHIP_1) || (region == TSWING_2_SHIP_2) || (region == TSWING_2_SHIP_3) ){ return MULTI_TS_SLOT_LIST; } if((region == TSWING_0_NAME_0) || (region == TSWING_0_NAME_1) || (region == TSWING_0_NAME_2) || (region == TSWING_0_NAME_3) || (region == TSWING_1_NAME_0) || (region == TSWING_1_NAME_1) || (region == TSWING_1_NAME_2) || (region == TSWING_1_NAME_3) || (region == TSWING_2_NAME_0) || (region == TSWING_2_NAME_1) || (region == TSWING_2_NAME_2) || (region == TSWING_2_NAME_3) ){ return MULTI_TS_PLAYER_LIST; } if((region == TSWING_LIST_0) || (region == TSWING_LIST_1) || (region == TSWING_LIST_2) || (region == TSWING_LIST_3)){ return MULTI_TS_AVAIL_LIST; } return -1; } // convert the region num to a ship slot index int multi_ts_slot_index(int region) { switch(region){ case TSWING_0_SHIP_0: return 0; case TSWING_0_SHIP_1: return 1; case TSWING_0_SHIP_2: return 2; case TSWING_0_SHIP_3: return 3; case TSWING_1_SHIP_0: return 4; case TSWING_1_SHIP_1: return 5; case TSWING_1_SHIP_2: return 6; case TSWING_1_SHIP_3: return 7; case TSWING_2_SHIP_0: return 8; case TSWING_2_SHIP_1: return 9; case TSWING_2_SHIP_2: return 10; case TSWING_2_SHIP_3: return 11; } return -1; } // convert the region num to an avail list index (starting from absolute 0) int multi_ts_avail_index(int region) { switch(region){ case TSWING_LIST_0: return 0; case TSWING_LIST_1: return 1; case TSWING_LIST_2: return 2; case TSWING_LIST_3: return 3; } return -1; } // convert the region num to a player slot index int multi_ts_player_index(int region) { switch(region){ case TSWING_0_NAME_0: return 0; case TSWING_0_NAME_1: return 1; case TSWING_0_NAME_2: return 2; case TSWING_0_NAME_3: return 3; case TSWING_1_NAME_0: return 4; case TSWING_1_NAME_1: return 5; case TSWING_1_NAME_2: return 6; case TSWING_1_NAME_3: return 7; case TSWING_2_NAME_0: return 8; case TSWING_2_NAME_1: return 9; case TSWING_2_NAME_2: return 10; case TSWING_2_NAME_3: return 11; } return -1; } // blit any active ship information text void multi_ts_blit_ship_info() { int y_start, line_height; ship_info *sip; char str[100]; // if we don't have a valid ship selected, do nothing if(Multi_ts_select_ship_class == -1){ return; } // get the ship class sip = &Ship_info[Multi_ts_select_ship_class]; // starting line y_start = Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_Y_COORD]; line_height = gr_get_font_height() + 1; memset(str,0,100); // blit the ship class (name) gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Class",739), GR_RESIZE_MENU); if(strlen(sip->get_display_name())){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->get_display_name(), GR_RESIZE_MENU); } y_start += line_height; // blit the ship type gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Type",740), GR_RESIZE_MENU); if(sip->type_str){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->type_str.get(), GR_RESIZE_MENU); } y_start += line_height; // blit the ship length gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Length",741), GR_RESIZE_MENU); if(sip->ship_length){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->ship_length.get(), GR_RESIZE_MENU); } y_start += line_height; // blit the max velocity gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Max Velocity",742), GR_RESIZE_MENU); sprintf(str, XSTR("%d m/s",743),(int)sip->max_vel.xyz.z); gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start,str,GR_RESIZE_MENU); y_start += line_height; // blit the maneuverability gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Maneuverability",744), GR_RESIZE_MENU); if(sip->maneuverability_str){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->maneuverability_str.get(), GR_RESIZE_MENU); } y_start += line_height; // blit the armor gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Armor",745), GR_RESIZE_MENU); if(sip->armor_str){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->armor_str.get(), GR_RESIZE_MENU); } y_start += line_height; // blit the gun mounts gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Gun Mounts",746), GR_RESIZE_MENU); if(sip->gun_mounts){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->gun_mounts.get(), GR_RESIZE_MENU); } y_start += line_height; // blit the missile banke gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Missile Banks",747), GR_RESIZE_MENU); if(sip->missile_banks){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->missile_banks.get(), GR_RESIZE_MENU); } y_start += line_height; // blit the manufacturer gr_set_color_fast(&Color_normal); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD], y_start, XSTR("Manufacturer",748), GR_RESIZE_MENU); if(sip->manufacturer_str){ gr_set_color_fast(&Color_bright); gr_string(Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_X_COORD] + 150, y_start, sip->manufacturer_str.get(), GR_RESIZE_MENU); } y_start += line_height; // blit the _short_ text description Assert(Multi_ts_ship_info_line_count < 3); gr_set_color_fast(&Color_normal); for(int idx=0;idxflags & NETINFO_FLAG_OBSERVER){ Net_player->p_info.ship_index = -1; } // initialize all players and observer for(idx=0;idxobjnum]; if (objp->flags[Object::Object_Flags::Should_be_dead]) continue; // if its a ship, get its slot index (if any) multi_ts_get_team_and_slot(Ships[objp->instance].ship_name,&team_index,&slot_index); if((slot_index != -1) && (team_index != -1)){ Multi_ts_team[team_index].multi_ts_objnum[slot_index] = Ships[objp->instance].objnum; } } } // get the proper team and slot index for the given ship name void multi_ts_get_team_and_slot(char* ship_name, int* team_index, int* slot_index) { // set the return values to default values *team_index = -1; *slot_index = -1; // set up the ship registry pointer const ship_registry_entry* ship_regp = ship_registry_get(ship_name); // For a player usable ship, there has to be a parse object, a team and a position within a wing. Assert(ship_regp != nullptr && ship_regp->has_p_objp()); if (ship_regp == nullptr || !ship_regp->has_p_objp()) { return; } // this should send the original team, in case the team changes via sexp or scripting *team_index = ship_regp->p_objp()->loadout_team; // if we're in team vs. team mode if(Netgame.type_flags & NG_TYPE_TEAM){ // get the slot within the wing, since there's only one wing each in team vs. team. *slot_index = ship_regp->p_objp()->pos_in_wing; } // if we're _not_ in team vs. team mode else { // get the wing index first. int wing_index = ship_regp->p_objp()->wing_status_wing_index; // only if the wing index is valid do we set a slot index. if (wing_index >= 0 && wing_index < MAX_STARTING_WINGS) { *slot_index = wing_index * MULTI_TS_NUM_SHIP_SLOTS_TEAM + ship_regp->p_objp()->pos_in_wing; } } } // function to return the shipname of the ship in the slot designated by the team and slot // parameters void multi_ts_get_shipname( char *ship_name, int team, int slot_index ) { if ( Netgame.type_flags & NG_TYPE_TEAM ) { Assert( (team >= 0) && (team < MULTI_TS_MAX_TVT_TEAMS) ); wing_bash_ship_name(ship_name, TVT_wing_names[team], slot_index + 1); } else { Assert( team == 0 ); wing_bash_ship_name(ship_name, Starting_wing_names[slot_index / MULTI_TS_NUM_SHIP_SLOTS_TEAM], (slot_index % MULTI_TS_NUM_SHIP_SLOTS_TEAM) + 1); } } // assign the correct flags to the correct slots void multi_ts_init_flags() { int idx,s_idx; // zero out the flags for(idx=0;idx MULTI_TS_AVAIL_MAX_DISPLAY){ gamesnd_play_iface(InterfaceSounds::USER_SELECT); Multi_ts_avail_start++; } else { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } } // handle an available ship scroll up button press void multi_ts_avail_scroll_up() { if(Multi_ts_avail_start > 0){ gamesnd_play_iface(InterfaceSounds::USER_SELECT); Multi_ts_avail_start--; } else { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } } // handle all mouse events (clicking, dragging, and dropping) void multi_ts_handle_mouse() { int snazzy_region,snazzy_action; int region_type,region_index,region_empty; int mouse_x,mouse_y,ship_class; // get the mouse coords mouse_get_pos_unscaled(&mouse_x,&mouse_y); // do frame for the snazzy menu snazzy_region = snazzy_menu_do(Multi_ts_mask_data, Multi_ts_mask_w, Multi_ts_mask_h, Multi_ts_snazzy_regions, Multi_ts_region.data(), &snazzy_action, 0); region_type = -1; region_index = -1; region_empty = 1; ship_class = -1; if(snazzy_region != -1){ region_type = multi_ts_region_type(snazzy_region); Assert(region_type != -1); // determine what type of region the mouse is over and the appropriate index switch(region_type){ case MULTI_TS_AVAIL_LIST: region_index = multi_ts_avail_index(snazzy_region); ship_class = multi_ts_get_avail_ship_class(region_index); if(ship_class == -1){ region_empty = 1; } else { region_empty = (Ss_pool->value_or(ship_class, -1) > 0) ? 0 : 1; } break; case MULTI_TS_SLOT_LIST: region_index = multi_ts_slot_index(snazzy_region); if(region_index >= 0 ) { region_empty = (Multi_ts_team[Net_player->p_info.team].multi_ts_flag[region_index] >= 0) ? 0 : 1; if(!region_empty){ ship_class = Wss_slots[region_index].ship_class; } } break; case MULTI_TS_PLAYER_LIST: region_index = multi_ts_player_index(snazzy_region); if(region_index >= 0 ) { region_empty = (Multi_ts_team[Net_player->p_info.team].multi_ts_player[region_index] != NULL) ? 0 : 1; } break; } } // maybe play a "highlight" sound switch(region_type){ case MULTI_TS_PLAYER_LIST: if((Multi_ts_hotspot_index != region_index) && (region_index >= 0) && (Multi_ts_team[Net_player->p_info.team].multi_ts_player[region_index] != NULL)){ gamesnd_play_iface(InterfaceSounds::USER_SELECT); } break; } // set the current frame mouse hotspot vars Multi_ts_hotspot_type = region_type; Multi_ts_hotspot_index = region_index; // if we currently have clicked on something and have just released it if(!Multi_ts_carried_flag && Multi_ts_clicked_flag && !mouse_down(MOUSE_LEFT_BUTTON)){ Multi_ts_clicked_flag = 0; } // if we're currently not carrying anything and the user has clicked if(!Multi_ts_carried_flag && !Multi_ts_clicked_flag && mouse_down(MOUSE_LEFT_BUTTON) && !region_empty){ // set the "clicked" flag Multi_ts_clicked_flag = 1; // check to see if he clicked on a ship type and highlight if necessary switch(region_type){ // selected a ship in the wing slots case MULTI_TS_SLOT_LIST: Multi_ts_select_type = MULTI_TS_SLOT_LIST; Multi_ts_select_index = region_index; multi_ts_select_ship(); break; // selected a ship on the avail list case MULTI_TS_AVAIL_LIST: Multi_ts_select_type = MULTI_TS_AVAIL_LIST; Multi_ts_select_index = region_index; multi_ts_select_ship(); break; // selected something else - unselect default : Multi_ts_select_type = -1; Multi_ts_select_index = -1; Multi_ts_select_ship_class = -1; break; } Multi_ts_clicked_x = mouse_x; Multi_ts_clicked_y = mouse_y; } // if we had something clicked and have started dragging it if(!Multi_ts_carried_flag && Multi_ts_clicked_flag && mouse_down(MOUSE_LEFT_BUTTON) && ((Multi_ts_clicked_x != mouse_x) || (Multi_ts_clicked_y != mouse_y))){ // if this player is an observer, he shouldn't be able to do jack if(Net_player->flags & NETINFO_FLAG_OBSERVER){ return; } // first we check for illegal conditions (any case where he cannot grab what he is attempting to grab) switch(region_type){ case MULTI_TS_AVAIL_LIST : // if players are not yet locked, can't grab ships if(!Multi_ts_team[Net_player->p_info.team].multi_players_locked){ return; } if(region_empty){ return; } break; case MULTI_TS_SLOT_LIST: // if players are not yet locked, can't grab ships if(!Multi_ts_team[Net_player->p_info.team].multi_players_locked){ return; } if(multi_ts_disabled_slot(region_index)){ multi_ts_maybe_host_only_popup(); return; } if(region_index >= 0 ) { if(Multi_ts_team[Net_player->p_info.team].multi_ts_flag[region_index] < 0){ return; } } break; case MULTI_TS_PLAYER_LIST: if(!multi_ts_can_grab_player(region_index)){ return; } break; } Multi_ts_clicked_flag = 0; Multi_ts_carried_flag = 1; // set up the carried icon here Multi_ts_carried_from_type = region_type; Multi_ts_carried_from_index = region_index; Multi_ts_carried_ship_class = ship_class; } // if we were carrying something but have dropped it if(Multi_ts_carried_flag && !mouse_down(MOUSE_LEFT_BUTTON)){ Multi_ts_carried_flag = 0; Multi_ts_clicked_flag = 0; // if we're not allowed to drop onto this slot if((region_type == MULTI_TS_SLOT_LIST) && multi_ts_disabled_slot(region_index)){ multi_ts_maybe_host_only_popup(); } // if we're over some kind of valid region, apply multi_ts_drop(Multi_ts_carried_from_type,Multi_ts_carried_from_index,region_type,region_index,Multi_ts_carried_ship_class); } } // can the specified player perform the action he is attempting int multi_ts_can_perform(int from_type,int from_index,int to_type,int to_index,int ship_class,int player_index) { net_player *pl; int op_type; p_object *pobj; // get the appropriate player if(player_index == -1){ pl = Net_player; } else { pl = &Net_players[player_index]; } // get the operation type op_type = multi_ts_get_dnd_type(from_type,from_index,to_type,to_index,player_index); // if either of the indices are bogus, then bail if((from_index == -1) || (to_index == -1)){ return 0; } switch(op_type){ case TS_GRAB_FROM_LIST: // if there are no more of this ship class, its no go if(Ss_pool_teams[pl->p_info.team].value_or(ship_class, -1) <= 0){ return 0; } // if he's not allowed to touch the wing slot if(multi_ts_disabled_slot(to_index,player_index)){ return 0; } // if the slot he's trying to drop it on is "permanently" empty if(Multi_ts_team[pl->p_info.team].multi_ts_flag[to_index] == MULTI_TS_FLAG_NONE){ return 0; } break; case TS_SWAP_LIST_SLOT: // if there are no more of this ship class, its no go if(Ss_pool_teams[pl->p_info.team].value_or(ship_class, -1) <= 0){ return 0; } // if he's not allowed to touch the wing slot if(multi_ts_disabled_slot(to_index,player_index)){ return 0; } // if the slot we're trying to move to is invalid, then do nothing if(Multi_ts_team[pl->p_info.team].multi_ts_flag[to_index] == MULTI_TS_FLAG_NONE){ return 0; } break; case TS_SWAP_SLOT_SLOT: // if he's not allowed to touch one of the slots, its no go if(multi_ts_disabled_slot(from_index,player_index) || multi_ts_disabled_slot(to_index,player_index)){ return 0; } // if the slot we're taking from is invalid if(Multi_ts_team[pl->p_info.team].multi_ts_flag[to_index] == MULTI_TS_FLAG_NONE){ return 0; } break; case TS_DUMP_TO_LIST: // if he's not allowed to be touching the slot to begin with, it no go if(multi_ts_disabled_slot(from_index,player_index)){ return 0; } // if the slot we're trying to move to is invalid, then do nothing if(Multi_ts_team[pl->p_info.team].multi_ts_flag[to_index] == MULTI_TS_FLAG_NONE){ return 0; } break; case TS_SWAP_PLAYER_PLAYER: // if his team is already locked, he cannot do this if(Multi_ts_team[pl->p_info.team].multi_players_locked){ return 0; } // if there isn't a player at one of the positions if((Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] == NULL) || (Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] == NULL)){ return 0; } // if this is not a player ship type object if(Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index] != -1){ pobj = mission_parse_get_arrival_ship(Ships[Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].instance].ship_name); if((pobj == NULL) || !(pobj->flags[Mission::Parse_Object_Flags::OF_Player_start])){ return 0; } } if(Netgame.type_flags & NG_TYPE_TEAM){ // if he's not the team captain, he cannot do this if(!(pl->flags & NETINFO_FLAG_TEAM_CAPTAIN)){ return 0; } } else { // if he's not the host, he cannot do this if(!(pl->flags & NETINFO_FLAG_GAME_HOST)){ return 0; } } break; case TS_MOVE_PLAYER: // if his team is already locked, he cannot do this if(Multi_ts_team[pl->p_info.team].multi_players_locked){ return 0; } // if there isn't a player at the _from_ if(Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] == NULL){ return 0; } // if there is no ship at the _to_ location if(Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index] < 0){ return 0; } // if this is not a player ship type object if(Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index] != -1){ pobj = mission_parse_get_arrival_ship(Ships[Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].instance].ship_name); if((pobj == NULL) || !(pobj->flags[Mission::Parse_Object_Flags::OF_Player_start])){ return 0; } } if(Netgame.type_flags & NG_TYPE_TEAM){ // if he's not the team captain, he cannot do this if(!(pl->flags & NETINFO_FLAG_TEAM_CAPTAIN)){ return 0; } } else { // if he's not the host, he cannot do this if(!(pl->flags & NETINFO_FLAG_GAME_HOST)){ return 0; } } break; default : return 0; break; } return 1; } // determine the kind of drag and drop operation this is int multi_ts_get_dnd_type(int from_type,int /*from_index*/,int to_type,int to_index,int player_index) { net_player *pl; // get the appropriate player if(player_index == -1){ pl = Net_player; } else { pl = &Net_players[player_index]; } switch(from_type){ // came from the ship avail list case MULTI_TS_AVAIL_LIST : // do nothing if(to_type == MULTI_TS_AVAIL_LIST){ return -1; } // if placing it on a slot if(to_type == MULTI_TS_SLOT_LIST){ if(Wss_slots_teams[pl->p_info.team][to_index].ship_class == -1){ return TS_GRAB_FROM_LIST; } else { return TS_SWAP_LIST_SLOT; } } break; // came from the ship slots case MULTI_TS_SLOT_LIST : if(to_type == MULTI_TS_SLOT_LIST){ return TS_SWAP_SLOT_SLOT; } if(to_type == MULTI_TS_AVAIL_LIST){ return TS_DUMP_TO_LIST; } break; // came from the player lists case MULTI_TS_PLAYER_LIST : if(to_type == MULTI_TS_PLAYER_LIST){ if(Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] == NULL){ return TS_MOVE_PLAYER; } else { return TS_SWAP_PLAYER_PLAYER; } } break; } return -1; } void multi_ts_apply(int from_type,int from_index,int to_type,int to_index,int ship_class,int player_index) { int size,update; ubyte wss_data[MAX_PACKET_SIZE]; net_player *pl; // determine what kind of operation this is int type = multi_ts_get_dnd_type(from_type,from_index,to_type,to_index,player_index); // get the proper net player if(player_index == -1){ pl = Net_player; } else { pl = &Net_players[player_index]; } // set the proper pool pointers common_set_team_pointers(pl->p_info.team); interface_snd_id sound; switch(type){ case TS_SWAP_SLOT_SLOT : nprintf(("Network","Apply swap slot slot %d %d\n",from_index,to_index)); update = ss_swap_slot_slot(from_index,to_index,&sound); break; case TS_DUMP_TO_LIST : nprintf(("Network","Apply dump to list %d %d\n",from_index,to_index)); update = ss_dump_to_list(from_index,ship_class,&sound); break; case TS_SWAP_LIST_SLOT : nprintf(("Network","Apply swap list slot %d %d\n",from_index,to_index)); update = ss_swap_list_slot(ship_class,to_index,&sound); break; case TS_GRAB_FROM_LIST : nprintf(("Network","Apply grab from list %d %d\n",from_index,to_index)); update = ss_grab_from_list(ship_class,to_index,&sound); break; case TS_SWAP_PLAYER_PLAYER : nprintf(("Network","Apply swap player player %d %d\n",from_index,to_index)); update = multi_ts_swap_player_player(from_index,to_index,&sound,player_index); break; case TS_MOVE_PLAYER : nprintf(("Network","Apply move player %d %d\n",from_index,to_index)); update = multi_ts_move_player(from_index,to_index,&sound,player_index); break; default : update = 0; break; } if(update){ // if we're the host, send an update to all players if ( MULTIPLAYER_HOST ) { // send the correct type of update if(type == TS_SWAP_PLAYER_PLAYER){ } else { size = store_wss_data(wss_data, MAX_PACKET_SIZE-20, sound, player_index); send_wss_update_packet(pl->p_info.team,wss_data, size); // send a player slot update packet as well, so ship class information, etc is kept correct send_pslot_update_packet(pl->p_info.team,TS_CODE_PLAYER_UPDATE, interface_snd_id()); } // if the player index == -1, it means the action was done locally - so play a sound if((player_index == -1) && (sound.isValid())){ gamesnd_play_iface(sound); } } // sync the interface screen up ourselves, if necessary if(Net_player->p_info.team == pl->p_info.team){ multi_ts_sync_interface(); } // make sure all flags are set properly for all teams multi_ts_init_flags(); } // set the proper pool pointers common_set_team_pointers(Net_player->p_info.team); } // drop a carried icon void multi_ts_drop(int from_type,int from_index,int to_type,int to_index,int ship_class,int player_index) { // if I'm the host, apply immediately if(Net_player->flags & NETINFO_FLAG_GAME_HOST){ // if this is a legal operation if(multi_ts_can_perform(from_type,from_index,to_type,to_index,ship_class,player_index)){ multi_ts_apply(from_type,from_index,to_type,to_index,ship_class,player_index); } else { nprintf(("Network","Could not apply operation!\n")); } } // otherwise send a request to the host else { send_wss_request_packet(Net_player->player_id, from_type, from_index, to_type, to_index, -1, ship_class, WSS_SHIP_SELECT); } } // swap two player positions int multi_ts_swap_player_player(int from_index,int to_index,interface_snd_id *sound,int player_index) { net_player *pl,*temp; // get the proper player pointer if(player_index == -1){ pl = Net_player; } else { pl = &Net_players[player_index]; } // swap the players temp = Multi_ts_team[pl->p_info.team].multi_ts_player[to_index]; Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] = Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]; Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] = temp; // update netplayer information if necessary if(Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] != NULL){ Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]->p_info.ship_index = from_index; Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]->p_info.ship_class = Wss_slots_teams[pl->p_info.team][from_index].ship_class; multi_assign_player_ship(NET_PLAYER_INDEX(Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]),&Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]],Wss_slots_teams[pl->p_info.team][from_index].ship_class); } if(Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] != NULL){ Multi_ts_team[pl->p_info.team].multi_ts_player[to_index]->p_info.ship_index = to_index; Multi_ts_team[pl->p_info.team].multi_ts_player[to_index]->p_info.ship_class = Wss_slots_teams[pl->p_info.team][to_index].ship_class; multi_assign_player_ship(NET_PLAYER_INDEX(Multi_ts_team[pl->p_info.team].multi_ts_player[to_index]),&Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]],Wss_slots_teams[pl->p_info.team][to_index].ship_class); } // update ship flags Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].flags.remove(Object::Object_Flags::Could_be_player); Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].flags.remove(Object::Object_Flags::Player_ship); if(Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] != NULL){ Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].flags.set(Object::Object_Flags::Player_ship); } Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]].flags.remove(Object::Object_Flags::Could_be_player); Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]].flags.remove(Object::Object_Flags::Player_ship); if(Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] != NULL){ Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]].flags.set(Object::Object_Flags::Player_ship); } // recalcalate which slots are locked/unlocked, etc ss_recalc_multiplayer_slots(); // send an update packet to all players if(Net_player->flags & NETINFO_FLAG_GAME_HOST){ send_pslot_update_packet(pl->p_info.team,TS_CODE_PLAYER_UPDATE,InterfaceSounds::ICON_DROP_ON_WING); gamesnd_play_iface(InterfaceSounds::ICON_DROP_ON_WING); } *sound = InterfaceSounds::ICON_DROP; return 1; } // move a player int multi_ts_move_player(int from_index,int to_index,interface_snd_id *sound,int player_index) { net_player *pl; // get the proper player pointer if(player_index == -1){ pl = Net_player; } else { pl = &Net_players[player_index]; } // swap the players Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] = Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]; Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] = NULL; // update netplayer information if necessary if(Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] != NULL){ Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]->p_info.ship_index = from_index; Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]->p_info.ship_class = Wss_slots_teams[pl->p_info.team][from_index].ship_class; multi_assign_player_ship(NET_PLAYER_INDEX(Multi_ts_team[pl->p_info.team].multi_ts_player[from_index]),&Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]],Wss_slots_teams[pl->p_info.team][from_index].ship_class); } if(Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] != NULL){ Multi_ts_team[pl->p_info.team].multi_ts_player[to_index]->p_info.ship_index = to_index; Multi_ts_team[pl->p_info.team].multi_ts_player[to_index]->p_info.ship_class = Wss_slots_teams[pl->p_info.team][to_index].ship_class; multi_assign_player_ship(NET_PLAYER_INDEX(Multi_ts_team[pl->p_info.team].multi_ts_player[to_index]),&Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]],Wss_slots_teams[pl->p_info.team][to_index].ship_class); } // update ship flags Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].flags.remove(Object::Object_Flags::Could_be_player); Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].flags.remove(Object::Object_Flags::Player_ship); if(Multi_ts_team[pl->p_info.team].multi_ts_player[to_index] != NULL){ Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[to_index]].flags.set(Object::Object_Flags::Player_ship); } Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]].flags.remove(Object::Object_Flags::Could_be_player); Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]].flags.remove(Object::Object_Flags::Player_ship); if(Multi_ts_team[pl->p_info.team].multi_ts_player[from_index] != NULL){ Objects[Multi_ts_team[pl->p_info.team].multi_ts_objnum[from_index]].flags.set(Object::Object_Flags::Player_ship); } // recalcalate which slots are locked/unlocked, etc ss_recalc_multiplayer_slots(); // send an update packet to all players if(Net_player->flags & NETINFO_FLAG_GAME_HOST){ send_pslot_update_packet(pl->p_info.team,TS_CODE_PLAYER_UPDATE,InterfaceSounds::ICON_DROP_ON_WING); gamesnd_play_iface(InterfaceSounds::ICON_DROP_ON_WING); } *sound = InterfaceSounds::ICON_DROP; return 1; } // get the ship class of the current index in the avail list or -1 if none exists int multi_ts_get_avail_ship_class(int index) { int ship_count = index + Multi_ts_avail_start; // find the Nth class with ships still available (the map iterates in ascending class // order, which is the same order the avail list is rendered in) for(const auto &[ship_class, count] : *Ss_pool){ if(count > 0){ if(ship_count == 0){ return ship_class; } ship_count--; } } return -1; } // blit the currently carried icon (if any) void multi_ts_blit_carried_icon() { int x,y; int offset_x,offset_y,callsign_w; char callsign[CALLSIGN_LEN+2]; // if we're not carrying anything, then return if(!Multi_ts_carried_flag){ return; } // get the mouse position mouse_get_pos_unscaled(&x,&y); // if we're carrying an icon of some kind switch(Multi_ts_carried_from_type){ case MULTI_TS_SLOT_LIST: offset_x = Multi_ts_slot_icon_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_X_COORD] - Multi_ts_clicked_x; offset_y = Multi_ts_slot_icon_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_Y_COORD] - Multi_ts_clicked_y; // blit the icon ss_blit_ship_icon(x + offset_x,y + offset_y,Multi_ts_carried_ship_class,0); break; case MULTI_TS_AVAIL_LIST: offset_x = Multi_ts_avail_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_X_COORD] - Multi_ts_clicked_x; offset_y = Multi_ts_avail_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_Y_COORD] - Multi_ts_clicked_y; // blit the icon ss_blit_ship_icon(x + offset_x,y + offset_y,Multi_ts_carried_ship_class,0); break; case MULTI_TS_PLAYER_LIST: // get the final length of the string so we can calculate a valid offset strcpy_s(callsign,Multi_ts_team[Net_player->p_info.team].multi_ts_player[Multi_ts_carried_from_index]->m_player->callsign); font::force_fit_string(callsign,CALLSIGN_LEN,Multi_ts_slot_text_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_W_COORD]); gr_get_string_size(&callsign_w,NULL,callsign); // calculate the offsets offset_x = (Multi_ts_slot_text_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_X_COORD] - Multi_ts_clicked_x) + ((Multi_ts_slot_text_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_W_COORD] - callsign_w)/2); offset_y = Multi_ts_slot_text_coords[Multi_ts_carried_from_index][gr_screen.res][MULTI_TS_Y_COORD] - Multi_ts_clicked_y; gr_set_color_fast(&Color_normal); gr_string(x + offset_x,y + offset_y,Multi_ts_team[Net_player->p_info.team].multi_ts_player[Multi_ts_carried_from_index]->m_player->callsign,GR_RESIZE_MENU); break; default : break; } } // if the (console) player is allowed to grab a player slot at this point int multi_ts_can_grab_player(int slot_index, int player_index) { net_player *pl; // get a pointe rto the proper net player if(player_index == -1){ pl = Net_player; } else { pl = &Net_players[player_index]; } // if the players are locked in any case, he annot grab it if(Multi_ts_team[pl->p_info.team].multi_players_locked){ return 0; } if(Netgame.type_flags & NG_TYPE_TEAM){ // if he's not the team captain, he cannot do this if(!(pl->flags & NETINFO_FLAG_TEAM_CAPTAIN)){ return 0; } } else { // if he's not the host, he cannot do this if(!(pl->flags & NETINFO_FLAG_GAME_HOST)){ return 0; } } // if the slot is empty if(Multi_ts_team[pl->p_info.team].multi_ts_player[slot_index] == NULL){ return 0; } return 1; } // get the team # of the given ship int multi_ts_get_team(char *ship_name) { int idx;//,s_idx; // lookup through all team ship names Assert(MAX_TVT_WINGS == MULTI_TS_MAX_TVT_TEAMS); for(idx=0;idxp_info.ship_index == slot_index){ return ICON_FRAME_PLAYER; } // if its otherwise being lit by the mouse if((Multi_ts_hotspot_type == MULTI_TS_SLOT_LIST) && (Multi_ts_hotspot_index == slot_index)){ return ICON_FRAME_HOT; } // normal unhighlighted frame return ICON_FRAME_NORMAL; } // select the given slot and setup any information, etc void multi_ts_select_ship() { int n_lines; int n_chars[MAX_BRIEF_LINES]; const char *p_str[MAX_BRIEF_LINES]; char *token; // blast all current text memset(Multi_ts_ship_info_lines,0,MULTI_TS_SHIP_INFO_MAX_TEXT); memset(Multi_ts_ship_info_text,0,MULTI_TS_SHIP_INFO_MAX_TEXT); // get the selected ship class Assert(Multi_ts_select_index >= 0); Multi_ts_select_ship_class = -1; switch(Multi_ts_select_type){ case MULTI_TS_SLOT_LIST: Multi_ts_select_ship_class = Wss_slots[Multi_ts_select_index].ship_class; break; case MULTI_TS_AVAIL_LIST: Multi_ts_select_ship_class = multi_ts_get_avail_ship_class(Multi_ts_select_index); // if he has selected an empty slot, don't do anything if(Multi_ts_select_ship_class < 0){ return; } break; default : // should always have one of the 2 above types selected Int3(); break; } // split the text info up Assert(Multi_ts_select_ship_class >= 0); if (Ship_info[Multi_ts_select_ship_class].desc) { // strip out newlines auto ship_desc = util::unique_copy(Ship_info[Multi_ts_select_ship_class].desc.get(), true); token = strtok(ship_desc.get(), "\n"); if(token != NULL){ strcpy_s(Multi_ts_ship_info_text,token); while(token != NULL){ token = strtok(NULL,"\n"); if(token != NULL){ strcat_s(Multi_ts_ship_info_text," "); strcat_s(Multi_ts_ship_info_text,token); } } } if(Multi_ts_ship_info_text[0] != '\0'){ // split the string into multiple lines n_lines = split_str(Multi_ts_ship_info_text, Multi_ts_ship_info_coords[gr_screen.res][MULTI_TS_W_COORD], n_chars, p_str, MULTI_TS_SHIP_INFO_MAX_LINES, MULTI_TS_SHIP_INFO_MAX_LINE_LEN,0); // copy the split up lines into the text lines array for (int idx = 0;idxp_info.team].multi_players_locked){ extern int red_alert_mission(); // skip this for red-alert missions too since nothing is selectable if ( !red_alert_mission() && (multi_num_players() != 1) ) { popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,POPUP_OK, XSTR("Players have not yet been assigned to their ships",751)); return commit_pressed_status::MULTI_PLAYERS_NO_SHIPS; } else { Multi_ts_team[Net_player->p_info.team].multi_players_locked = 1; } } commit_pressed_status rc = commit_pressed_status::GENERAL_FAIL; // check to see if its not ok for this player to commit switch(multi_ts_ok_to_commit()){ // yes, it _is_ ok to commit case 0: rc = commit_pressed(); break; // player has not assigned all necessary ships case 1: gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); rc = commit_pressed_status::MULTI_NOT_ALL_ASSIGNED; popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,POPUP_OK, XSTR("You have not yet assigned all necessary ships",752)); break; // there are ships without primary weapons case 2: gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); rc = commit_pressed_status::MULTI_NO_PRIMARY; popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,POPUP_OK, XSTR("There are ships without primary weapons!",753)); break; // there are ships without secondary weapons case 3: gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); rc = commit_pressed_status::MULTI_NO_SECONDARY; popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,POPUP_OK, XSTR("There are ships without secondary weapons!",754)); break; } return rc; } // is it ok for this player to commit int multi_ts_ok_to_commit() { int idx,s_idx; int primary_ok,secondary_ok; // if this player is an observer, he can always commit if(Net_player->flags & NETINFO_FLAG_OBSERVER){ return 0; } for(idx=0;idxp_info.team].multi_ts_objnum[idx] != -1) && // implies player can't touch this slot anyway !multi_ts_disabled_slot(idx) && // implies that there should be a player ship here but there isn't ((Multi_ts_team[Net_player->p_info.team].multi_ts_player[idx] != NULL) && (Multi_ts_team[Net_player->p_info.team].multi_ts_flag[idx] == MULTI_TS_FLAG_EMPTY)) ){ return 1; } // if the ship in this slot has a ship which can be a player but has 0 primary or secondary weapons, then he cannot continue if( (Multi_ts_team[Net_player->p_info.team].multi_ts_objnum[idx] != -1) && ((Objects[Multi_ts_team[Net_player->p_info.team].multi_ts_objnum[idx]].flags[Object::Object_Flags::Could_be_player]) || (Objects[Multi_ts_team[Net_player->p_info.team].multi_ts_objnum[idx]].flags[Object::Object_Flags::Player_ship])) && !multi_ts_disabled_slot(idx)){ primary_ok = 0; secondary_ok = 0; // go through all weapons in the list for(s_idx=0;s_idxp_info.team][idx].wep[s_idx] >= 0) && (Wss_slots_teams[Net_player->p_info.team][idx].wep_count[s_idx] > 0)){ switch(Weapon_info[Wss_slots_teams[Net_player->p_info.team][idx].wep[s_idx]].subtype){ case WP_LASER: primary_ok = 1; break; case WP_MISSILE: secondary_ok = 1; break; default : Int3(); } } // if we've got both primary and secondary weapons if(primary_ok && secondary_ok){ break; } } // if the ship doesn't have primary weapons if (!primary_ok && !(The_mission.ai_profile->flags[AI::Profile_Flags::Multi_allow_empty_secondaries])) { return 2; } // if the ship doesn't have secondary weapons if (!secondary_ok && !(The_mission.ai_profile->flags[AI::Profile_Flags::Multi_allow_empty_secondaries])) { return 3; } } } return 0; } // check to see that no illegal ship settings have occurred void multi_ts_check_errors() { /* int idx; ship *shipp; for(idx=0;idxweapons.current_primary_bank != -1) && (shipp->weapons.current_secondary_bank != -1)); } */ } // ------------------------------------------------------------------------------------------------------ // TEAM SELECT PACKET HANDLERS // // send a player slot position update void send_pslot_update_packet(int team,int code, interface_snd_id sound) { ubyte data[MAX_PACKET_SIZE],stop,val; short s_sound; int idx; int packet_size = 0; int i_tmp; // build the header and add the data BUILD_HEADER(SLOT_UPDATE); // add the opcode val = (ubyte)code; ADD_DATA(val); // add the team val = (ubyte)team; ADD_DATA(val); // add the sound to play s_sound = (short)sound.value(); ADD_SHORT(s_sound); // add data based upon the packet code switch(code){ case TS_CODE_LOCK_TEAM: // don't have to do anything break; case TS_CODE_PLAYER_UPDATE: // only the host should ever be doing this Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST); // add individual slot data for(idx=0;idx(Wss_slots_teams[team][idx].ship_class)); // add the objnum we're working with i_tmp = Multi_ts_team[team].multi_ts_objnum[idx]; ADD_INT(i_tmp); // add a byte indicating if a player is here or not if(Multi_ts_team[team].multi_ts_player[idx] == NULL){ val = 0; } else { val = 1; } ADD_DATA(val); // if there's a player, add his address if(val){ ADD_SHORT(Multi_ts_team[team].multi_ts_player[idx]->player_id); // should also update his p_info settings locally Multi_ts_team[team].multi_ts_player[idx]->p_info.ship_class = Wss_slots_teams[team][idx].ship_class; Multi_ts_team[team].multi_ts_player[idx]->p_info.ship_index = idx; } // add a byte indicating what object flag should be set (0 == ~(OF_COULD_BE_PLAYER | OF_PLAYER_SHIP), 1 == player ship, 2 == could be player ship) if(Objects[Multi_ts_team[team].multi_ts_objnum[idx]].flags[Object::Object_Flags::Could_be_player]){ val = 2; } else if(Objects[Multi_ts_team[team].multi_ts_objnum[idx]].flags[Object::Object_Flags::Player_ship]){ val = 1; } else { val = 0; } ADD_DATA(val); } } // add a final stop byte val = 0xff; ADD_DATA(val); break; default : Int3(); break; } // send the packet to the standalone if(Net_player->flags & NETINFO_FLAG_AM_MASTER) { multi_io_send_to_all_reliable(data, packet_size); } else { multi_io_send_reliable(Net_player, data, packet_size); } } // process a player slot position update void process_pslot_update_packet(ubyte *data, header *hinfo) { int offset = HEADER_LENGTH; int my_index; int player_index,idx,team,code,objnum; short sound_id; short player_id; ubyte stop, val, slot_num; short ship_class; my_index = Net_player->p_info.ship_index; // if we're the standalone, then we should be routing this data to all the other clients player_index = -1; if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // fill in the address information of where this came from player_index = find_player_index(hinfo->id); Assert(player_index != -1); } // get the opcode GET_DATA(val); code = (int)val; // get the team GET_DATA(val); team = (int)val; // get the sound to play GET_SHORT(sound_id); auto sound = interface_snd_id(sound_id); // process the different opcodes switch(code){ case TS_CODE_LOCK_TEAM: // lock the team Multi_ts_team[team].multi_players_locked = 1; // if this was my team, sync stuff up if((team == Net_player->p_info.team) && !(Game_mode & GM_STANDALONE_SERVER)){ multi_ts_set_status_bar_mode(1); multi_ts_sync_interface(); ss_recalc_multiplayer_slots(); } // if this is the standalone server, we need to re-route the packet here and there if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // in team vs team mode, only a team captain should ever be sending this if(Netgame.type_flags & NG_TYPE_TEAM){ Assert(Net_players[player_index].flags & NETINFO_FLAG_TEAM_CAPTAIN); } // in any other mode, it better be coming from the game host else { Assert(Net_players[player_index].flags & NETINFO_FLAG_GAME_HOST); } // re-route to all other players for(idx=0;idxp_info.ship_index){ wl_reset_selected_slot(); my_index = Net_player->p_info.ship_index; } } } else { Multi_ts_team[team].multi_ts_player[slot_num] = NULL; } // get the ship flag byte GET_DATA(val); Objects[objnum].flags.remove(Object::Object_Flags::Player_ship); Objects[objnum].flags.remove(Object::Object_Flags::Could_be_player); switch(val){ case 1 : Objects[objnum].flags.set(Object::Object_Flags::Player_ship); break; case 2 : obj_set_flags( &Objects[objnum], Objects[objnum].flags + Object::Object_Flags::Could_be_player); break; } // get the next stop byte GET_DATA(stop); } // if we have a sound we're supposed to play if((sound.isValid()) && !(Game_mode & GM_STANDALONE_SERVER) && (gameseq_get_state() == GS_STATE_TEAM_SELECT)){ gamesnd_play_iface(sound); } // if i'm the standalone server, I should rebroadcast this packet if(Game_mode & GM_STANDALONE_SERVER){ for(idx=0;idx