diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 1ecc1ccec..cad8b91ea 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,9 @@ D1X-Rebirth Changelog +20110209 +-------- +main/ai.c, main/game.c, main/gamecntl.c, main/menu.c, main/multi.c, main/multi.h, main/state.c, main/state.h, main/titles.c: Reintroduced Savegames for Coop games using the original Descent2 implementation but correctly handling player slots in their pre-loading state preventing accidential player shifting which never really worked in the original game and we do not want with UDP anyways - was all tested but still might need a fix or two; Added some missing initializations for saving players and AI stuff; Completely ripped out remnants of saving between levels code + 20110206 -------- arch/include/key.h, main/automap.c: Increased key repeat values to react a little more like the MS-DOS version of the game but a little slower so I can still stop at the correct item; In automap reorganized control_info swapping as well as wiggle state handling to properly work in connection with the new input handling and Multiplayer where game is not paused diff --git a/main/ai.c b/main/ai.c index 31fb16ff7..5d336700f 100644 --- a/main/ai.c +++ b/main/ai.c @@ -3363,6 +3363,7 @@ int ai_save_state(PHYSFS_file *fp) { ai_local_rw *ail_rw; MALLOC(ail_rw, ai_local_rw, 1); + memset(ail_rw, 0, sizeof(ai_local_rw)); state_ai_local_to_ai_local_rw(&Ai_local_info[i], ail_rw); PHYSFS_write(fp, ail_rw, sizeof(ai_local_rw), 1); d_free(ail_rw); @@ -3373,6 +3374,7 @@ int ai_save_state(PHYSFS_file *fp) { ai_cloak_info_rw *aic_rw; MALLOC(aic_rw, ai_cloak_info_rw, 1); + memset(aic_rw, 0, sizeof(ai_cloak_info_rw)); state_ai_cloak_info_to_ai_cloak_info_rw(&Ai_cloak_info[i], aic_rw); PHYSFS_write(fp, aic_rw, sizeof(ai_cloak_info_rw), 1); d_free(aic_rw); diff --git a/main/game.c b/main/game.c index 89735cf11..d391122f4 100644 --- a/main/game.c +++ b/main/game.c @@ -783,11 +783,16 @@ void show_netgame_help() int nitems = 0; newmenu_item *m; - MALLOC(m, newmenu_item, 13); + MALLOC(m, newmenu_item, 14); if (!m) return; m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "F1\t THIS SCREEN"; +#if !(defined(__APPLE__) || defined(macintosh)) + m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "Alt-F2/F3\t SAVE/LOAD COOP GAME"; +#else + m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "Alt-F2/F3 (\x85-SHIFT-s/\x85-o)\t SAVE/LOAD COOP GAME"; +#endif m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "ALT-F4\t SHOW RETICLE NAMES"; m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "F7\t TOGGLE KILL LIST"; m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "F8\t SEND MESSAGE"; diff --git a/main/gamecntl.c b/main/gamecntl.c index ca4686955..fd856226c 100644 --- a/main/gamecntl.c +++ b/main/gamecntl.c @@ -620,12 +620,15 @@ int HandleSystemKey(int key) KEY_MAC(case KEY_COMMAND+KEY_SHIFTED+KEY_S:) KEY_MAC(case KEY_COMMAND+KEY_ALTED+KEY_2:) case KEY_ALTED+KEY_F2: - if (!Player_is_dead && !((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP))) - state_save_all( 0, 0 ); + if (!Player_is_dead) + state_save_all( 0 ); break; KEY_MAC(case KEY_COMMAND+KEY_S:) - case KEY_ALTED+KEY_F1: if (!Player_is_dead) state_save_all(0, 1); break; + case KEY_ALTED+KEY_F1: + if (!Player_is_dead) + state_save_all(1); + break; KEY_MAC(case KEY_COMMAND+KEY_O:) KEY_MAC(case KEY_COMMAND+KEY_ALTED+KEY_3:) case KEY_ALTED+KEY_F3: diff --git a/main/menu.c b/main/menu.c index d87a9c892..9965b3d37 100644 --- a/main/menu.c +++ b/main/menu.c @@ -640,12 +640,13 @@ int do_option ( int select) void delete_player_saved_games(char * name) { int i; - char filename[FILENAME_LEN + 9]; + char filename[PATH_MAX]; for (i=0;i<10; i++) { - sprintf( filename, GameArg.SysUsePlayersDir? "Players/%s.sg%x" : "%s.sg%x", name, i ); - + snprintf( filename, PATH_MAX, GameArg.SysUsePlayersDir? "Players/%s.sg%x" : "%s.sg%x", name, i ); + PHYSFS_delete(filename); + snprintf( filename, PATH_MAX, GameArg.SysUsePlayersDir? "Players/%s.mg%x" : "%s.mg%x", name, i ); PHYSFS_delete(filename); } } diff --git a/main/multi.c b/main/multi.c index c7e5059db..06e5db32d 100644 --- a/main/multi.c +++ b/main/multi.c @@ -58,6 +58,7 @@ COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. #include "pstypes.h" #include "strutil.h" #include "u_mem.h" +#include "state.h" #ifdef USE_IPX #include "net_ipx.h" #endif @@ -83,6 +84,10 @@ void multi_powcap_cap_objects(); void multi_powcap_adjust_remote_cap(int pnum); void multi_new_bounty_target( int pnum ); void multi_do_bounty( char *buf ); +void multi_save_game(ubyte slot, uint id, char *desc); +void multi_restore_game(ubyte slot, uint id); +void multi_do_save_game(char *buf); +void multi_do_restore_game(char *buf); // // Global variables @@ -98,6 +103,7 @@ int Show_kill_list = 1; int Show_reticle_name = 1; fix Show_kill_list_timer = 0; +char PKilledFlags[MAX_NUM_NET_PLAYERS]; int Bounty_target = 0; // Target for bounty mode netgame int multi_sending_message = 0; @@ -194,8 +200,8 @@ int message_length[MULTI_MAX_TYPE+1] = { 10, // BOSS_ACTIONS 27, // ROBOT_POWERUPS 7, // HOSTAGE_DOOR - 2+24, //SAVE_GAME (ubyte slot, uint id, char name[20]) // obsolete - 2+4, //RESTORE_GAME (ubyte slot, uint id) // obsolete + 2+24, //SAVE_GAME (ubyte slot, uint id, char name[20]) + 2+4, //RESTORE_GAME (ubyte slot, uint id) -1, // MULTI_REQ_PLAYER - NEVER USED -1, // MULTI_SEND_PLAYER - NEVER USED MAX_POWERUP_TYPES+1, // MULTI_POWCAP_UPDATE @@ -578,6 +584,8 @@ void multi_compute_kill(int killer, int killed) killed_pnum = Objects[killed].id; + PKilledFlags[killed_pnum]=1; + Assert ((killed_pnum >= 0) && (killed_pnum < N_players)); if (Game_mode & GM_TEAM) @@ -1480,6 +1488,7 @@ multi_do_reappear(char *buf) multi_make_ghost_player(Objects[objnum].id); create_player_appearance_effect(&Objects[objnum]); + PKilledFlags[Objects[objnum].id]=0; } void @@ -1714,62 +1723,67 @@ multi_do_remobj(char *buf) } +void multi_disconnect_player(int pnum) +{ + int i, n = 0; + + if (!(Game_mode & GM_NETWORK)) + return + + digi_play_sample( SOUND_HUD_MESSAGE, F1_0 ); + + HUD_init_message(HM_MULTI, "%s %s", Players[pnum].callsign, TXT_HAS_LEFT_THE_GAME); + + switch (multi_protocol) + { +#ifdef USE_IPX + case MULTI_PROTO_IPX: + net_ipx_disconnect_player(pnum); + break; +#endif +#ifdef USE_UDP + case MULTI_PROTO_UDP: + net_udp_disconnect_player(pnum); + break; +#endif + default: + Error("Protocol handling missing in multi_disconnect_player\n"); + break; + } + + for (i = 0; i < N_players; i++) + if (Players[i].connected) n++; + if (n == 1) + { + HUD_init_message(HM_MULTI, "You are the only person remaining in this netgame"); + } + + // Bounty target left - select a new one + if( Game_mode & GM_BOUNTY && pnum == Bounty_target && multi_i_am_master() ) + { + /* Select a random number */ + int new_bounty_target = d_rand() % MAX_NUM_NET_PLAYERS; + + /* Make sure they're valid: Don't check against kill flags, + * just in case everyone's dead! */ + while( !Players[new_bounty_target].connected ) + new_bounty_target = d_rand() % MAX_NUM_NET_PLAYERS; + + /* Select new target */ + multi_new_bounty_target( new_bounty_target ); + + /* Send this new data */ + multi_send_bounty(); + } +} + 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(HM_MULTI, "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_LEFT_THE_GAME); - - switch (multi_protocol) - { -#ifdef USE_IPX - case MULTI_PROTO_IPX: - net_ipx_disconnect_player(buf[1]); - break; -#endif -#ifdef USE_UDP - case MULTI_PROTO_UDP: - net_udp_disconnect_player(buf[1]); - break; -#endif - default: - Error("Protocol handling missing in multi_do_quit\n"); - break; - } - - for (i = 0; i < N_players; i++) - if (Players[i].connected) n++; - if (n == 1) - { - HUD_init_message(HM_MULTI, "You are the only person remaining in this netgame"); - } - - // Bounty target left - select a new one - if( Game_mode & GM_BOUNTY && buf[1] == Bounty_target && multi_i_am_master() ) - { - /* Select a random number */ - int new_bounty_target = d_rand() % MAX_NUM_NET_PLAYERS; - - /* Make sure they're valid: Don't check against kill flags, - * just in case everyone's dead! */ - while( !Players[new_bounty_target].connected ) - new_bounty_target = d_rand() % MAX_NUM_NET_PLAYERS; - - /* Select new target */ - multi_new_bounty_target( new_bounty_target ); - - /* Send this new data */ - multi_send_bounty(); - } - } - - return; + if (!(Game_mode & GM_NETWORK)) + return; + multi_disconnect_player((int)buf[1]); } void @@ -2051,8 +2065,10 @@ multi_reset_player_object(object *objp) 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); + if (objp->type == OBJ_PLAYER) + objp->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE; + else + objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE); //Init render info @@ -2172,6 +2188,10 @@ multi_process_data(char *buf, int len) 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_POWCAP_UPDATE: if (!Endlevel_sequence) multi_do_powcap_update(buf); break; case MULTI_HEARTBEAT: @@ -2523,6 +2543,7 @@ multi_send_reappear() PUT_INTEL_SHORT(multibuf+1, Players[Player_num].objnum); multi_send_data(multibuf, 3, 1); + PKilledFlags[Player_num]=0; } void @@ -2892,6 +2913,9 @@ multi_prep_level(void) multi_consistency_error(1); + for (i=0;i= STATE_COMPATIBLE_VERSION) || (SWAPINT(version) >= STATE_COMPATIBLE_VERSION)) { // Read description PHYSFS_read(fp, desc[i], sizeof(char) * DESC_LENGTH, 1); @@ -643,7 +649,7 @@ int state_get_save_file(char * fname, char * dsc, int blind_save) return state_get_savegame_filename(fname, dsc, "Save Game", blind_save); } -int state_get_restore_file(char * fname ) +int state_get_restore_file(char * fname) { return state_get_savegame_filename(fname, NULL, "Select Game to Restore", 0); } @@ -779,26 +785,29 @@ int state_save_old_game(int slotnum, char * sg_name, player_rw * sg_player, // ----------------------------------------------------------------------------------- -int state_save_all(int between_levels, int blind_save) +int state_save_all(int blind_save) { int rval; - char filename[128], desc[DESC_LENGTH+1]; + char filename[PATH_MAX], desc[DESC_LENGTH+1]; -#ifdef NETWORK - if ( Game_mode & GM_MULTI ) { + if ( Game_mode & GM_MULTI ) + { + if (Game_mode & GM_MULTI_COOP) + multi_initiate_save_game(); return 0; } -#endif stop_time(); + memset(&filename, '\0', PATH_MAX); + memset(&desc, '\0', DESC_LENGTH+1); if (!state_get_save_file(filename, desc, blind_save)) { start_time(); return 0; } - rval = state_save_all_sub(filename, desc, between_levels); + rval = state_save_all_sub(filename, desc); if (rval) HUD_init_message(HM_DEFAULT, "Game saved"); @@ -806,7 +815,7 @@ int state_save_all(int between_levels, int blind_save) return rval; } -int state_save_all_sub(char *filename, char *desc, int between_levels) +int state_save_all_sub(char *filename, char *desc) { int i,j; PHYSFS_file *fp; @@ -836,6 +845,13 @@ int state_save_all_sub(char *filename, char *desc, int between_levels) i = STATE_VERSION; PHYSFS_write(fp, &i, sizeof(int), 1); +// Save Coop state_game_id and this Player's callsign. Oh the redundancy... we have this one later on but Coop games want to read this before loading a state so for easy access save this here, too + if (Game_mode & GM_MULTI_COOP) + { + PHYSFS_write(fp, &state_game_id, sizeof(uint), 1); + PHYSFS_write(fp, &Players[Player_num].callsign, sizeof(char)*CALLSIGN_LEN, 1); + } + //Save description PHYSFS_write(fp, desc, sizeof(char) * DESC_LENGTH, 1); @@ -887,7 +903,8 @@ int state_save_all_sub(char *filename, char *desc, int between_levels) } // Save the Between levels flag... - PHYSFS_write(fp, &between_levels, sizeof(int), 1); + i = 0; + PHYSFS_write(fp, &i, sizeof(int), 1); // Save the mission info... PHYSFS_write(fp, Current_mission_filename, 9 * sizeof(char), 1); @@ -906,6 +923,7 @@ int state_save_all_sub(char *filename, char *desc, int between_levels) { player_rw *pl_rw; MALLOC(pl_rw, player_rw, 1); + memset(pl_rw, 0, sizeof(player_rw)); state_player_to_player_rw(&Players[Player_num], pl_rw); PHYSFS_write(fp, pl_rw, sizeof(player_rw), 1); d_free(pl_rw); @@ -922,96 +940,94 @@ int state_save_all_sub(char *filename, char *desc, int between_levels) PHYSFS_write(fp, &Cheats_enabled, sizeof(int), 1); PHYSFS_write(fp, &Game_turbo_mode, sizeof(int), 1); - if ( !between_levels ) { - //Finish all morph objects - for (i=0; i<=Highest_object_index; i++ ) { - if ( (Objects[i].type != OBJ_NONE) && (Objects[i].render_type==RT_MORPH)) { - morph_data *md; - md = find_morph_data(&Objects[i]); - if (md) { - md->obj->control_type = md->morph_save_control_type; - md->obj->movement_type = md->morph_save_movement_type; - md->obj->render_type = RT_POLYOBJ; - md->obj->mtype.phys_info = md->morph_save_phys_info; - md->obj = NULL; - } else { //maybe loaded half-morphed from disk - Objects[i].flags |= OF_SHOULD_BE_DEAD; - Objects[i].render_type = RT_POLYOBJ; - Objects[i].control_type = CT_NONE; - Objects[i].movement_type = MT_NONE; - } + for (i=0; i<=Highest_object_index; i++ ) { + if ( (Objects[i].type != OBJ_NONE) && (Objects[i].render_type==RT_MORPH)) { + morph_data *md; + md = find_morph_data(&Objects[i]); + if (md) { + md->obj->control_type = md->morph_save_control_type; + md->obj->movement_type = md->morph_save_movement_type; + md->obj->render_type = RT_POLYOBJ; + md->obj->mtype.phys_info = md->morph_save_phys_info; + md->obj = NULL; + } else { //maybe loaded half-morphed from disk + Objects[i].flags |= OF_SHOULD_BE_DEAD; + Objects[i].render_type = RT_POLYOBJ; + Objects[i].control_type = CT_NONE; + Objects[i].movement_type = MT_NONE; } } - - //Save object info - i = Highest_object_index+1; - PHYSFS_write(fp, &i, sizeof(int), 1); - //PHYSFS_write(fp, Objects, sizeof(object), i); - for (i = 0; i <= Highest_object_index; i++) - { - object_rw *obj_rw; - MALLOC(obj_rw, object_rw, 1); - state_object_to_object_rw(&Objects[i], obj_rw); - PHYSFS_write(fp, obj_rw, sizeof(object_rw), 1); - d_free(obj_rw); - } - - //Save wall info - i = Num_walls; - PHYSFS_write(fp, &i, sizeof(int), 1); - PHYSFS_write(fp, Walls, sizeof(wall), i); - - //Save door info - i = Num_open_doors; - PHYSFS_write(fp, &i, sizeof(int), 1); - PHYSFS_write(fp, ActiveDoors, sizeof(active_door), i); - - //Save trigger info - PHYSFS_write(fp, &Num_triggers, sizeof(int), 1); - PHYSFS_write(fp, Triggers, sizeof(trigger), Num_triggers); - - //Save tmap info - for (i = 0; i <= Highest_segment_index; i++) - { - for (j = 0; j < 6; j++) - { - PHYSFS_write(fp, &Segments[i].sides[j].wall_num, sizeof(short), 1); - PHYSFS_write(fp, &Segments[i].sides[j].tmap_num, sizeof(short), 1); - PHYSFS_write(fp, &Segments[i].sides[j].tmap_num2, sizeof(short), 1); - } - } - - // Save the fuelcen info - PHYSFS_write(fp, &Control_center_destroyed, sizeof(int), 1); - PHYSFS_write(fp, &Countdown_seconds_left, sizeof(int), 1); - PHYSFS_write(fp, &Num_robot_centers, sizeof(int), 1); - PHYSFS_write(fp, RobotCenters, sizeof(matcen_info), Num_robot_centers); - PHYSFS_write(fp, &ControlCenterTriggers, sizeof(control_center_triggers), 1); - PHYSFS_write(fp, &Num_fuelcenters, sizeof(int), 1); - for (i = 0; i < Num_fuelcenters; i++) - { - // NOTE: Usually Descent1 handles countdown by Timer value of the Reactor Station. Since we now use Descent2 code to handle countdown (which we do in case there IS NO Reactor Station which causes potential trouble in Multiplayer), let's find the Reactor here and store the timer in it. - if (Station[i].Type == SEGMENT_IS_CONTROLCEN) - Station[i].Timer = Countdown_timer; - PHYSFS_write(fp, &Station[i], sizeof(FuelCenter), 1); - } -// PHYSFS_write(fp, Station, sizeof(FuelCenter), Num_fuelcenters); - - // Save the control cen info - PHYSFS_write(fp, &Control_center_been_hit, sizeof(int), 1); - PHYSFS_write(fp, &Control_center_player_been_seen, sizeof(int), 1); - PHYSFS_write(fp, &Control_center_next_fire_time, sizeof(int), 1); - PHYSFS_write(fp, &Control_center_present, sizeof(int), 1); - PHYSFS_write(fp, &Dead_controlcen_object_num, sizeof(int), 1); - - // Save the AI state - ai_save_state( fp ); - - // Save the automap visited info - PHYSFS_write(fp, Automap_visited, sizeof(ubyte), MAX_SEGMENTS); - } + +//Save object info + i = Highest_object_index+1; + PHYSFS_write(fp, &i, sizeof(int), 1); + //PHYSFS_write(fp, Objects, sizeof(object), i); + for (i = 0; i <= Highest_object_index; i++) + { + object_rw *obj_rw; + MALLOC(obj_rw, object_rw, 1); + memset(obj_rw, 0, sizeof(object_rw)); + state_object_to_object_rw(&Objects[i], obj_rw); + PHYSFS_write(fp, obj_rw, sizeof(object_rw), 1); + d_free(obj_rw); + } + +//Save wall info + i = Num_walls; + PHYSFS_write(fp, &i, sizeof(int), 1); + PHYSFS_write(fp, Walls, sizeof(wall), i); + +//Save door info + i = Num_open_doors; + PHYSFS_write(fp, &i, sizeof(int), 1); + PHYSFS_write(fp, ActiveDoors, sizeof(active_door), i); + +//Save trigger info + PHYSFS_write(fp, &Num_triggers, sizeof(int), 1); + PHYSFS_write(fp, Triggers, sizeof(trigger), Num_triggers); + +//Save tmap info + for (i = 0; i <= Highest_segment_index; i++) + { + for (j = 0; j < 6; j++) + { + PHYSFS_write(fp, &Segments[i].sides[j].wall_num, sizeof(short), 1); + PHYSFS_write(fp, &Segments[i].sides[j].tmap_num, sizeof(short), 1); + PHYSFS_write(fp, &Segments[i].sides[j].tmap_num2, sizeof(short), 1); + } + } + +// Save the fuelcen info + PHYSFS_write(fp, &Control_center_destroyed, sizeof(int), 1); + PHYSFS_write(fp, &Countdown_seconds_left, sizeof(int), 1); + PHYSFS_write(fp, &Num_robot_centers, sizeof(int), 1); + PHYSFS_write(fp, RobotCenters, sizeof(matcen_info), Num_robot_centers); + PHYSFS_write(fp, &ControlCenterTriggers, sizeof(control_center_triggers), 1); + PHYSFS_write(fp, &Num_fuelcenters, sizeof(int), 1); + for (i = 0; i < Num_fuelcenters; i++) + { + // NOTE: Usually Descent1 handles countdown by Timer value of the Reactor Station. Since we now use Descent2 code to handle countdown (which we do in case there IS NO Reactor Station which causes potential trouble in Multiplayer), let's find the Reactor here and store the timer in it. + if (Station[i].Type == SEGMENT_IS_CONTROLCEN) + Station[i].Timer = Countdown_timer; + PHYSFS_write(fp, &Station[i], sizeof(FuelCenter), 1); + } +// PHYSFS_write(fp, Station, sizeof(FuelCenter), Num_fuelcenters); + +// Save the control cen info + PHYSFS_write(fp, &Control_center_been_hit, sizeof(int), 1); + PHYSFS_write(fp, &Control_center_player_been_seen, sizeof(int), 1); + PHYSFS_write(fp, &Control_center_next_fire_time, sizeof(int), 1); + PHYSFS_write(fp, &Control_center_present, sizeof(int), 1); + PHYSFS_write(fp, &Dead_controlcen_object_num, sizeof(int), 1); + +// Save the AI state + ai_save_state( fp ); + +// Save the automap visited info + PHYSFS_write(fp, Automap_visited, sizeof(ubyte), MAX_SEGMENTS); + PHYSFS_write(fp, &state_game_id, sizeof(uint), 1); PHYSFS_write(fp, &Laser_rapid_fire, sizeof(int), 1); PHYSFS_write(fp, &Ugly_robot_cheat, sizeof(int), 1); @@ -1019,6 +1035,35 @@ int state_save_all_sub(char *filename, char *desc, int between_levels) PHYSFS_write(fp, &Physics_cheat_flag, sizeof(int), 1); PHYSFS_write(fp, &Lunacy, sizeof(int), 1); +// Save Coop Info + if (Game_mode & GM_MULTI_COOP) + { + for (i = 0; i < MAX_PLAYERS; i++) + { + player_rw *pl_rw; + MALLOC(pl_rw, player_rw, 1); + memset(pl_rw, 0, sizeof(player_rw)); + state_player_to_player_rw(&Players[i], pl_rw); + PHYSFS_write(fp, pl_rw, sizeof(player_rw), 1); + d_free(pl_rw); + } + PHYSFS_write(fp, &Netgame.mission_title, sizeof(char), MISSION_NAME_LEN+1); + PHYSFS_write(fp, &Netgame.mission_name, sizeof(char), 9); + PHYSFS_write(fp, &Netgame.levelnum, sizeof(int), 1); + PHYSFS_write(fp, &Netgame.difficulty, sizeof(ubyte), 1); + PHYSFS_write(fp, &Netgame.game_status, sizeof(ubyte), 1); + PHYSFS_write(fp, &Netgame.numplayers, sizeof(ubyte), 1); + PHYSFS_write(fp, &Netgame.max_numplayers, sizeof(ubyte), 1); + PHYSFS_write(fp, &Netgame.numconnected, sizeof(ubyte), 1); + for (i = 0; i < MAX_PLAYERS; i++) + PHYSFS_write(fp, &Netgame.killed[i], sizeof(short), 1); + PHYSFS_write(fp, &Netgame.level_time, sizeof(int), 1); + for (i = 0; i < MAX_PLAYERS; i++) + PHYSFS_write(fp, &Netgame.player_score[i], sizeof(int), 1); + for (i = 0; i < MAX_PLAYERS; i++) + PHYSFS_write(fp, &Netgame.player_flags[i], sizeof(ubyte), 1); + } + PHYSFS_close(fp); start_time(); @@ -1029,13 +1074,7 @@ int state_save_all_sub(char *filename, char *desc, int between_levels) // ----------------------------------------------------------------------------------- int state_restore_all(int in_game) { - char filename[128]; - -#ifdef NETWORK - if ( Game_mode & GM_MULTI ) { - return 0; - } -#endif + char filename[PATH_MAX]; if ( Newdemo_state == ND_STATE_RECORDING ) newdemo_stop_recording(); @@ -1043,6 +1082,13 @@ int state_restore_all(int in_game) if ( Newdemo_state != ND_STATE_NORMAL ) return 0; + if ( Game_mode & GM_MULTI ) + { + if (Game_mode & GM_MULTI_COOP) + multi_initiate_restore_game(); + return 0; + } + stop_time(); if (!state_get_restore_file(filename)) { start_time(); @@ -1067,17 +1113,17 @@ int state_restore_all_sub(char *filename) { int ObjectStartLocation; int BogusSaturnShit = 0; - int version,i, j, segnum; + int version,i, j, segnum, coop_player_got[MAX_PLAYERS]; object * obj; PHYSFS_file *fp; int swap = 0; // if file is not endian native, have to swap all shorts and ints int current_level, next_level; - int between_levels; char mission[16]; char desc[DESC_LENGTH+1]; char id[5]; char org_callsign[CALLSIGN_LEN+16]; fix tmptime32 = 0; + player_rw *pl_rw; #ifndef NDEBUG if (GameArg.SysUsePlayersDir && strncmp(filename, "Players/", 8)) @@ -1108,6 +1154,19 @@ int state_restore_all_sub(char *filename) return 0; } +// Read Coop state_game_id. Oh the redundancy... we have this one later on but Coop games want to read this before loading a state so for easy access we have this here + if (Game_mode & GM_MULTI_COOP) + { + char saved_callsign[CALLSIGN_LEN]; + state_game_id = PHYSFSX_readSXE32(fp, swap); + PHYSFS_read(fp, &saved_callsign, sizeof(char)*CALLSIGN_LEN, 1); + if (strcmp(saved_callsign, Players[Player_num].callsign)) // check the callsign of the palyer who saved this state. It MUST match. If we transferred this savegame from pilot A to pilot B, others won't be able to restore us. So bail out here if this is the case. + { + PHYSFS_close(fp); + return 0; + } + } + // Read description PHYSFS_read(fp, desc, sizeof(char) * DESC_LENGTH, 1); @@ -1115,7 +1174,8 @@ int state_restore_all_sub(char *filename) PHYSFS_seek(fp, PHYSFS_tell(fp) + THUMBNAIL_W * THUMBNAIL_H); // Read the Between levels flag... - between_levels = PHYSFSX_readSXE32(fp, swap); + i = PHYSFSX_readSXE32(fp, swap); + i = 0; // Read the mission info... PHYSFS_read(fp, mission, sizeof(char) * 9, 1); @@ -1135,49 +1195,36 @@ int state_restore_all_sub(char *filename) GameTime64 = (fix64)tmptime32; // Start new game.... - Game_mode = GM_NORMAL; + if (!(Game_mode & GM_MULTI_COOP)) + { + Game_mode = GM_NORMAL; #ifdef NETWORK - change_playernum_to(0); + change_playernum_to(0); #endif - strcpy( org_callsign, Players[0].callsign ); - N_players = 1; - InitPlayerObject(); //make sure player's object set up - init_player_stats_game(); //clear all stats + N_players = 1; + strcpy( org_callsign, Players[0].callsign ); + InitPlayerObject(); //make sure player's object set up + init_player_stats_game(); //clear all stats + } + else // in coop we want to stay the player we are already. + { + strcpy( org_callsign, Players[Player_num].callsign ); + init_player_stats_game(); + } if (Game_wind) window_set_visible(Game_wind, 0); //Read player info - if ( between_levels ) { - int saved_offset; - player_rw *pl_rw; - MALLOC(pl_rw, player_rw, 1); - PHYSFS_read(fp, pl_rw, sizeof(player_rw), 1); - player_rw_swap(pl_rw, swap); - state_player_rw_to_player(pl_rw, &Players[Player_num]); - d_free(pl_rw); - saved_offset = PHYSFS_tell(fp); - PHYSFS_close( fp ); - do_briefing_screens(Briefing_text_filename, next_level); - fp = PHYSFSX_openReadBuffered(filename); - PHYSFS_seek(fp, saved_offset); - StartNewLevelSub( next_level, 1);//use page_in_textures here to fix OGL texture precashing crash -MPM - } else { - player_rw *pl_rw; - StartNewLevelSub(current_level, 1);//use page_in_textures here to fix OGL texture precashing crash -MPM - MALLOC(pl_rw, player_rw, 1); - PHYSFS_read(fp, pl_rw, sizeof(player_rw), 1); - player_rw_swap(pl_rw, swap); - state_player_rw_to_player(pl_rw, &Players[Player_num]); - d_free(pl_rw); - } + StartNewLevelSub(current_level, 1);//use page_in_textures here to fix OGL texture precashing crash -MPM + MALLOC(pl_rw, player_rw, 1); + PHYSFS_read(fp, pl_rw, sizeof(player_rw), 1); + player_rw_swap(pl_rw, swap); + state_player_rw_to_player(pl_rw, &Players[Player_num]); + d_free(pl_rw); strcpy( Players[Player_num].callsign, org_callsign ); -// Set the right level - if ( between_levels ) - Players[Player_num].level = next_level; - // Restore the weapon states PHYSFS_read(fp, &Primary_weapon, sizeof(sbyte), 1); PHYSFS_read(fp, &Secondary_weapon, sizeof(sbyte), 1); @@ -1193,139 +1240,137 @@ int state_restore_all_sub(char *filename) Cheats_enabled = PHYSFSX_readSXE32(fp, swap); Game_turbo_mode = PHYSFSX_readSXE32(fp, swap); - if ( !between_levels ) { - Do_appearance_effect = 0; // Don't do this for middle o' game stuff. + Do_appearance_effect = 0; // Don't do this for middle o' game stuff. - ObjectStartLocation = PHYSFS_tell(fp); + ObjectStartLocation = PHYSFS_tell(fp); RetryObjectLoading: - //Clear out all the objects from the lvl file - for (segnum=0; segnum <= Highest_segment_index; segnum++) - Segments[segnum].objects = -1; - reset_objects(1); - - //Read objects, and pop 'em into their respective segments. - i = PHYSFSX_readSXE32(fp, swap); - Highest_object_index = i-1; - if ( !BogusSaturnShit ) - //object_read_n_swap(Objects, i, swap, fp); - for (i=0; i<=Highest_object_index; i++ ) - { - object_rw *obj_rw; - MALLOC(obj_rw, object_rw, 1); - PHYSFS_read(fp, obj_rw, sizeof(object_rw), 1); - object_rw_swap(obj_rw, swap); - state_object_rw_to_object(obj_rw, &Objects[i]); - d_free(obj_rw); - } - else { - for (i=0; i<=Highest_object_index; i++ ) - { - object_rw *obj_rw; - MALLOC(obj_rw, object_rw, 1); - // Insert 3 bytes after the read in obj->rtype.pobj_info.alt_textures field. - PHYSFS_read(fp, obj_rw, sizeof(object_rw)-3, 1); - object_rw_swap(obj_rw, swap); - state_object_rw_to_object(obj_rw, &Objects[i]); - d_free(obj_rw); - Objects[i].rtype.pobj_info.alt_textures = -1; - } - } - - for (i=0; i<=Highest_object_index; i++ ) { - obj = &Objects[i]; - obj->rtype.pobj_info.alt_textures = -1; - segnum = obj->segnum; - obj->next = obj->prev = obj->segnum = -1; - if ( obj->type != OBJ_NONE ) { - // Check for a bogus Saturn version!!!! - if (!BogusSaturnShit ) { - if ( (segnum<0) || (segnum>Highest_segment_index) ) { - BogusSaturnShit = 1; - PHYSFS_seek( fp, ObjectStartLocation ); - goto RetryObjectLoading; - } - } - obj_link(i,segnum); - } - } - special_reset_objects(); - - //Restore wall info - Num_walls = PHYSFSX_readSXE32(fp, swap); - // Check for a bogus Saturn version!!!! - if (!BogusSaturnShit ) { - if ( (Num_walls<0) || (Num_walls>MAX_WALLS) ) { - BogusSaturnShit = 1; - PHYSFS_seek( fp, ObjectStartLocation ); - goto RetryObjectLoading; - } - } + //Clear out all the objects from the lvl file + for (segnum=0; segnum <= Highest_segment_index; segnum++) + Segments[segnum].objects = -1; + reset_objects(1); - wall_read_n_swap(Walls, Num_walls, swap, fp); - // Check for a bogus Saturn version!!!! - if (!BogusSaturnShit ) { - for (i=0; iHighest_segment_index) || (Walls[i].sidenum<-1) || (Walls[i].sidenum>5) ) { + //Read objects, and pop 'em into their respective segments. + i = PHYSFSX_readSXE32(fp, swap); + Highest_object_index = i-1; + if ( !BogusSaturnShit ) + //object_read_n_swap(Objects, i, swap, fp); + for (i=0; i<=Highest_object_index; i++ ) + { + object_rw *obj_rw; + MALLOC(obj_rw, object_rw, 1); + PHYSFS_read(fp, obj_rw, sizeof(object_rw), 1); + object_rw_swap(obj_rw, swap); + state_object_rw_to_object(obj_rw, &Objects[i]); + d_free(obj_rw); + } + else { + for (i=0; i<=Highest_object_index; i++ ) + { + object_rw *obj_rw; + MALLOC(obj_rw, object_rw, 1); + // Insert 3 bytes after the read in obj->rtype.pobj_info.alt_textures field. + PHYSFS_read(fp, obj_rw, sizeof(object_rw)-3, 1); + object_rw_swap(obj_rw, swap); + state_object_rw_to_object(obj_rw, &Objects[i]); + d_free(obj_rw); + Objects[i].rtype.pobj_info.alt_textures = -1; + } + } + + for (i=0; i<=Highest_object_index; i++ ) { + obj = &Objects[i]; + obj->rtype.pobj_info.alt_textures = -1; + segnum = obj->segnum; + obj->next = obj->prev = obj->segnum = -1; + if ( obj->type != OBJ_NONE ) { + // Check for a bogus Saturn version!!!! + if (!BogusSaturnShit ) { + if ( (segnum<0) || (segnum>Highest_segment_index) ) { BogusSaturnShit = 1; PHYSFS_seek( fp, ObjectStartLocation ); goto RetryObjectLoading; } } + obj_link(i,segnum); } + } + special_reset_objects(); - //Restore door info - Num_open_doors = PHYSFSX_readSXE32(fp, swap); - active_door_read_n_swap(ActiveDoors, Num_open_doors, swap, fp); - - //Restore trigger info - Num_triggers = PHYSFSX_readSXE32(fp, swap); - trigger_read_n_swap(Triggers, Num_triggers, swap, fp); - - //Restore tmap info - for (i=0; i<=Highest_segment_index; i++ ) { - for (j=0; j<6; j++ ) { - Segments[i].sides[j].wall_num = PHYSFSX_readSXE16(fp, swap); - Segments[i].sides[j].tmap_num = PHYSFSX_readSXE16(fp, swap); - Segments[i].sides[j].tmap_num2 = PHYSFSX_readSXE16(fp, swap); + //Restore wall info + Num_walls = PHYSFSX_readSXE32(fp, swap); + // Check for a bogus Saturn version!!!! + if (!BogusSaturnShit ) { + if ( (Num_walls<0) || (Num_walls>MAX_WALLS) ) { + BogusSaturnShit = 1; + PHYSFS_seek( fp, ObjectStartLocation ); + goto RetryObjectLoading; + } + } + + wall_read_n_swap(Walls, Num_walls, swap, fp); + // Check for a bogus Saturn version!!!! + if (!BogusSaturnShit ) { + for (i=0; iHighest_segment_index) || (Walls[i].sidenum<-1) || (Walls[i].sidenum>5) ) { + BogusSaturnShit = 1; + PHYSFS_seek( fp, ObjectStartLocation ); + goto RetryObjectLoading; } } - - //Restore the fuelcen info - Control_center_destroyed = PHYSFSX_readSXE32(fp, swap); - Countdown_seconds_left = PHYSFSX_readSXE32(fp, swap); - Num_robot_centers = PHYSFSX_readSXE32(fp, swap); - matcen_info_read_n_swap(RobotCenters, Num_robot_centers, swap, fp); - control_center_triggers_read_n_swap(&ControlCenterTriggers, 1, swap, fp); - Num_fuelcenters = PHYSFSX_readSXE32(fp, swap); - fuelcen_read_n_swap(Station, Num_fuelcenters, swap, fp); - Countdown_timer = 0; - for (i = 0; i < Num_fuelcenters; i++) - { - // NOTE: Usually Descent1 handles countdown by Timer value of the Reactor Station. Since we now use Descent2 code to handle countdown (which we do in case there IS NO Reactor Station which causes potential trouble in Multiplayer), let's find the Reactor here and read the timer from it. - if (Station[i].Type == SEGMENT_IS_CONTROLCEN) - Countdown_timer = Station[i].Timer; - } - - // Restore the control cen info - Control_center_been_hit = PHYSFSX_readSXE32(fp, swap); - Control_center_player_been_seen = PHYSFSX_readSXE32(fp, swap); - Control_center_next_fire_time = PHYSFSX_readSXE32(fp, swap); - Control_center_present = PHYSFSX_readSXE32(fp, swap); - Dead_controlcen_object_num = PHYSFSX_readSXE32(fp, swap); - - // Restore the AI state - ai_restore_state( fp, swap ); - - // Restore the automap visited info - PHYSFS_read(fp, Automap_visited, sizeof(ubyte), MAX_SEGMENTS); - - // Restore hacked up weapon system stuff. - Auto_fire_fusion_cannon_time = 0; - Next_laser_fire_time = GameTime64; - Next_missile_fire_time = GameTime64; - Last_laser_fired_time = GameTime64; - } + + //Restore door info + Num_open_doors = PHYSFSX_readSXE32(fp, swap); + active_door_read_n_swap(ActiveDoors, Num_open_doors, swap, fp); + + //Restore trigger info + Num_triggers = PHYSFSX_readSXE32(fp, swap); + trigger_read_n_swap(Triggers, Num_triggers, swap, fp); + + //Restore tmap info + for (i=0; i<=Highest_segment_index; i++ ) { + for (j=0; j<6; j++ ) { + Segments[i].sides[j].wall_num = PHYSFSX_readSXE16(fp, swap); + Segments[i].sides[j].tmap_num = PHYSFSX_readSXE16(fp, swap); + Segments[i].sides[j].tmap_num2 = PHYSFSX_readSXE16(fp, swap); + } + } + + //Restore the fuelcen info + Control_center_destroyed = PHYSFSX_readSXE32(fp, swap); + Countdown_seconds_left = PHYSFSX_readSXE32(fp, swap); + Num_robot_centers = PHYSFSX_readSXE32(fp, swap); + matcen_info_read_n_swap(RobotCenters, Num_robot_centers, swap, fp); + control_center_triggers_read_n_swap(&ControlCenterTriggers, 1, swap, fp); + Num_fuelcenters = PHYSFSX_readSXE32(fp, swap); + fuelcen_read_n_swap(Station, Num_fuelcenters, swap, fp); + Countdown_timer = 0; + for (i = 0; i < Num_fuelcenters; i++) + { + // NOTE: Usually Descent1 handles countdown by Timer value of the Reactor Station. Since we now use Descent2 code to handle countdown (which we do in case there IS NO Reactor Station which causes potential trouble in Multiplayer), let's find the Reactor here and read the timer from it. + if (Station[i].Type == SEGMENT_IS_CONTROLCEN) + Countdown_timer = Station[i].Timer; + } + + // Restore the control cen info + Control_center_been_hit = PHYSFSX_readSXE32(fp, swap); + Control_center_player_been_seen = PHYSFSX_readSXE32(fp, swap); + Control_center_next_fire_time = PHYSFSX_readSXE32(fp, swap); + Control_center_present = PHYSFSX_readSXE32(fp, swap); + Dead_controlcen_object_num = PHYSFSX_readSXE32(fp, swap); + + // Restore the AI state + ai_restore_state( fp, swap ); + + // Restore the automap visited info + PHYSFS_read(fp, Automap_visited, sizeof(ubyte), MAX_SEGMENTS); + + // Restore hacked up weapon system stuff. + Auto_fire_fusion_cannon_time = 0; + Next_laser_fire_time = GameTime64; + Next_missile_fire_time = GameTime64; + Last_laser_fired_time = GameTime64; + state_game_id = 0; if ( version >= 7 ) { @@ -1340,6 +1385,76 @@ RetryObjectLoading: do_lunacy_on(); } +// Read Coop Info + if (Game_mode & GM_MULTI_COOP) + { + player restore_players[MAX_PLAYERS]; + int coop_player_slot_remap[MAX_PLAYERS], coop_got_nplayers = 0; + + for (i = 0; i < MAX_PLAYERS; i++) + { + player_rw *pl_rw; + object *obj; + + // prepare arrays for mapping our players below + coop_player_slot_remap[i] = -1; + coop_player_got[i] = 0; + + // read the stored players + MALLOC(pl_rw, player_rw, 1); + PHYSFS_read(fp, pl_rw, sizeof(player_rw), 1); + player_rw_swap(pl_rw, swap); + state_player_rw_to_player(pl_rw, &restore_players[i]); + d_free(pl_rw); + + // make all (previous) player objects to ghosts + obj = &Objects[restore_players[i].objnum]; + obj->type = OBJ_GHOST; + multi_reset_player_object(obj); + + } + for (i = 0; i < MAX_PLAYERS; i++) // copy restored players to the current slots + { + for (j = 0; j < MAX_PLAYERS; j++) + { + // map stored players to current players depending on their unique (which we made sure) callsign + if (Players[i].connected == CONNECT_PLAYING && restore_players[j].connected == CONNECT_PLAYING && !strcmp(Players[i].callsign, restore_players[j].callsign)) + { + object *obj; + memcpy(&Players[i], &restore_players[j], sizeof(player)); + coop_player_slot_remap[i] = j; + coop_player_got[i] = 1; + coop_got_nplayers++; + obj = &Objects[Players[i].objnum]; + obj->id = i; // assign player object id to player number + // make this restored player object an actual player again + obj->type = OBJ_PLAYER; + multi_reset_player_object(obj); + } + } + } + PHYSFS_read(fp, &Netgame.mission_title, sizeof(char), MISSION_NAME_LEN+1); + PHYSFS_read(fp, &Netgame.mission_name, sizeof(char), 9); + Netgame.levelnum = PHYSFSX_readSXE32(fp, swap); + PHYSFS_read(fp, &Netgame.difficulty, sizeof(ubyte), 1); + PHYSFS_read(fp, &Netgame.game_status, sizeof(ubyte), 1); + PHYSFS_read(fp, &Netgame.numplayers, sizeof(ubyte), 1); + PHYSFS_read(fp, &Netgame.max_numplayers, sizeof(ubyte), 1); + MaxNumNetPlayers = Netgame.max_numplayers; + PHYSFS_read(fp, &Netgame.numconnected, sizeof(ubyte), 1); + for (i = 0; i < MAX_PLAYERS; i++) + Netgame.killed[coop_player_slot_remap[i]] = PHYSFSX_readSXE16(fp, swap); + Netgame.level_time = PHYSFSX_readSXE32(fp, swap); + for (i = 0; i < MAX_PLAYERS; i++) + Netgame.player_score[coop_player_slot_remap[i]] = PHYSFSX_readSXE32(fp, swap); + for (i = 0; i < MAX_PLAYERS; i++) + PHYSFS_read(fp, &Netgame.player_flags[coop_player_slot_remap[i]], sizeof(ubyte), 1); + for (i = 0; i < MAX_PLAYERS; i++) // Disconnect connected players not available in this Savegame + if (!coop_player_got[i] && Players[i].connected == CONNECT_PLAYING) + multi_disconnect_player(i); + Viewer = ConsoleObject = &Objects[Players[Player_num].objnum]; // make sure Viewer and ConsoleObject are set up (which we skipped by not using InitPlayerObject but we need since objects changed while loading) + } + PHYSFS_close(fp); if (Game_wind) @@ -1349,3 +1464,52 @@ RetryObjectLoading: return 1; } + +int state_get_game_id(char *filename) +{ + int version; + PHYSFS_file *fp; + int swap = 0; // if file is not endian native, have to swap all shorts and ints + char id[5], saved_callsign[CALLSIGN_LEN]; + + #ifndef NDEBUG + if (GameArg.SysUsePlayersDir && strncmp(filename, "Players/", 8)) + Int3(); + #endif + + if (!(Game_mode & GM_MULTI_COOP)) + return 0; + + fp = PHYSFSX_openReadBuffered(filename); + if ( !fp ) return 0; + +//Read id + PHYSFS_read(fp, id, sizeof(char) * 4, 1); + if ( memcmp( id, dgss_id, 4 )) { + PHYSFS_close(fp); + return 0; + } + +//Read version + //Check for swapped file here, as dgss_id is written as a string (i.e. endian independent) + PHYSFS_read(fp, &version, sizeof(int), 1); + if (version & 0xffff0000) + { + swap = 1; + version = SWAPINT(version); + } + + if (version < STATE_COMPATIBLE_VERSION) { + PHYSFS_close(fp); + return 0; + } + +// Read Coop state_game_id to validate the savegame we are about to load matches the others + state_game_id = PHYSFSX_readSXE32(fp, swap); + PHYSFS_read(fp, &saved_callsign, sizeof(char)*CALLSIGN_LEN, 1); + if (strcmp(saved_callsign, Players[Player_num].callsign)) // check the callsign of the palyer who saved this state. It MUST match. If we transferred this savegame from pilot A to pilot B, others won't be able to restore us. So bail out here if this is the case. + return 0; + + return state_game_id; +} + diff --git a/main/state.h b/main/state.h index 7fde50595..01e4a4464 100644 --- a/main/state.h +++ b/main/state.h @@ -22,21 +22,22 @@ COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. #define _STATE_H #include "playsave.h" -int state_save_all(int between_levels, int blind_save); +int state_save_all(int blind_save); int state_restore_all(int in_game ); extern int state_save_old_game(int slotnum, char * sg_name, player_rw * sg_player, int sg_difficulty_level, int sg_primary_weapon, int sg_secondary_weapon, int sg_next_level_num ); -int state_save_all_sub(char *filename, char *desc, int between_levels); +int state_save_all_sub(char *filename, char *desc); int state_restore_all_sub(char *filename); extern uint state_game_id; extern int state_quick_item; int state_get_save_file(char * fname, char * dsc, int blind_save); -int state_get_restore_file(char * fname ); +int state_get_restore_file(char * fname); +int state_get_game_id(char *filename); #endif diff --git a/main/titles.c b/main/titles.c index 6342a8e74..5e1e406a8 100644 --- a/main/titles.c +++ b/main/titles.c @@ -52,12 +52,10 @@ static char rcsid[] = "$Id: titles.c,v 1.2 2006/03/18 23:08:13 michaelstather Ex #include "piggy.h" #include "songs.h" #include "newmenu.h" -#include "state.h" #include "menu.h" #include "mouse.h" #include "console.h" -void title_save_game(); void set_briefing_fontcolor (); #define MAX_BRIEFING_COLORS 7 @@ -1017,37 +1015,6 @@ int new_briefing_screen(briefing *br, int first) return 1; } - -//----------------------------------------------------------------------------- -void title_save_game() -{ - grs_canvas * save_canv; - grs_canvas * save_canv_data; - grs_font * save_font; - ubyte palette[768]; - - if ( Next_level_num == 0 ) return; - - save_canv = grd_curcanv; - save_font = grd_curcanv->cv_font; - - save_canv_data = gr_create_canvas( grd_curcanv->cv_bitmap.bm_w, grd_curcanv->cv_bitmap.bm_h ); - gr_set_current_canvas(save_canv_data); - gr_ubitmap(0,0,&save_canv->cv_bitmap); - gr_set_current_canvas(save_canv); - gr_clear_canvas(gr_find_closest_color_current( 0, 0, 0)); - gr_palette_read( palette ); - gr_palette_load( gr_palette ); -#ifndef SHAREWARE - state_save_all(1, 0); -#endif - - gr_set_current_canvas(save_canv); - gr_ubitmap(0,0,&save_canv_data->cv_bitmap); - gr_palette_load( palette ); - gr_set_curfont(save_font); -} - //----------------------------------------------------------------------------- int briefing_handler(window *wind, d_event *event, briefing *br) { @@ -1084,10 +1051,6 @@ int briefing_handler(window *wind, d_event *event, briefing *br) switch (key) { - case KEY_ALTED+KEY_F2: - title_save_game(); - return 1; - case KEY_ESC: window_close(wind); return 1;