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

This commit is contained in:
zicodxx 2011-02-09 12:58:28 +01:00
parent 2232433860
commit eb0d876b9c
10 changed files with 744 additions and 371 deletions

View file

@ -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

View file

@ -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);

View file

@ -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";

View file

@ -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:

View file

@ -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);
}
}

View file

@ -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<MAX_NUM_NET_PLAYERS;i++)
PKilledFlags[i]=0;
for (i = 0; i < NumNetPlayerPositions; i++)
{
if (i != Player_num)
@ -3152,6 +3176,17 @@ void change_playernum_to( int new_Player_num )
Player_num = new_Player_num;
}
int multi_all_players_alive()
{
int i;
for (i=0;i<N_players;i++)
{
if (PKilledFlags[i] && Players[i].connected)
return (0);
}
return (1);
}
void multi_send_powcap_update ()
{
int i;
@ -3343,6 +3378,200 @@ void multi_new_bounty_target( int pnum )
digi_play_sample( SOUND_CONTROL_CENTER_WARNING_SIREN, F1_0 * 3 );
}
void multi_do_save_game(char *buf)
{
int count = 1;
ubyte slot;
uint id;
char desc[25];
slot = *(ubyte *)(buf+count); count += 1;
id = GET_INTEL_INT(buf+count); count += 4;
memcpy( desc, &buf[count], 20 ); count += 20;
multi_save_game( slot, id, desc );
}
void multi_do_restore_game(char *buf)
{
int count = 1;
ubyte slot;
uint id;
slot = *(ubyte *)(buf+count); count += 1;
id = GET_INTEL_INT(buf+count); count += 4;
multi_restore_game( slot, id );
}
void multi_send_save_game(ubyte slot, uint id, char * desc)
{
int count = 0;
multibuf[count] = MULTI_SAVE_GAME; count += 1;
multibuf[count] = slot; count += 1; // Save slot=0
PUT_INTEL_INT( multibuf+count, id ); count += 4; // Save id
memcpy( &multibuf[count], desc, 20 ); count += 20;
multi_send_data(multibuf, count, 1);
}
void multi_send_restore_game(ubyte slot, uint id)
{
int count = 0;
multibuf[count] = MULTI_RESTORE_GAME; count += 1;
multibuf[count] = slot; count += 1; // Save slot=0
PUT_INTEL_INT( multibuf+count, id ); count += 4; // Save id
multi_send_data(multibuf, count, 1);
}
void multi_initiate_save_game()
{
fix game_id = 0;
int i, j, slot;
char filename[PATH_MAX];
char desc[24];
if (multi_protocol == MULTI_PROTO_IPX)
return;
if ((Endlevel_sequence) || (Control_center_destroyed))
return;
if (!multi_i_am_master())
{
HUD_init_message(HM_MULTI, "Only host is allowed to save a game!");
return;
}
if (!multi_all_players_alive())
{
HUD_init_message(HM_MULTI, "Can't save! All players must be alive!");
return;
}
for (i = 0; i < N_players; i++)
{
for (j = 0; j < N_players; j++)
{
if (i != j && !stricmp(Players[i].callsign, Players[j].callsign))
{
HUD_init_message(HM_MULTI, "Can't save! Multiple players with same callsign!");
return;
}
}
}
memset(&filename, '\0', PATH_MAX);
memset(&desc, '\0', 24);
slot = state_get_save_file(filename, desc, 0 );
if (!slot)
return;
slot--;
// Make a unique game id
game_id = ((fix)timer_query());
game_id ^= N_players<<4;
for (i = 0; i < N_players; i++ )
game_id ^= *(fix *)Players[i].callsign;
if ( game_id == 0 )
game_id = 1; // 0 is invalid
multi_send_save_game( slot, game_id, desc );
multi_do_frame();
multi_save_game( slot,game_id, desc );
}
extern int state_get_game_id(char *);
void multi_initiate_restore_game()
{
int i, j, slot;
char filename[PATH_MAX];
if (multi_protocol == MULTI_PROTO_IPX)
return;
if ((Endlevel_sequence) || (Control_center_destroyed))
return;
if (!multi_i_am_master())
{
HUD_init_message(HM_MULTI, "Only host is allowed to load a game!");
return;
}
if (!multi_all_players_alive())
{
HUD_init_message(HM_MULTI, "Can't load! All players must be alive!");
return;
}
for (i = 0; i < N_players; i++)
{
for (j = 0; j < N_players; j++)
{
if (i != j && !stricmp(Players[i].callsign, Players[j].callsign))
{
HUD_init_message(HM_MULTI, "Can't load! Multiple players with same callsign!");
return;
}
}
}
slot = state_get_restore_file(filename);
if (!slot)
return;
state_game_id = state_get_game_id(filename);
if (!state_game_id)
return;
slot--;
multi_send_restore_game(slot,state_game_id);
multi_do_frame();
multi_restore_game(slot,state_game_id);
}
void multi_save_game(ubyte slot, uint id, char *desc)
{
char filename[PATH_MAX];
if ((Endlevel_sequence) || (Control_center_destroyed))
return;
snprintf(filename, PATH_MAX, GameArg.SysUsePlayersDir? "Players/%s.mg%d" : "%s.mg%d", Players[Player_num].callsign, slot);
HUD_init_message(HM_MULTI, "Saving game #%d, '%s'", slot, desc);
stop_time();
state_game_id = id;
state_save_all_sub(filename, desc );
}
void multi_restore_game(ubyte slot, uint id)
{
char filename[PATH_MAX];
player saved_player;
int pnum,i;
int thisid;
if ((Endlevel_sequence) || (Control_center_destroyed))
return;
saved_player = Players[Player_num];
snprintf(filename, PATH_MAX, GameArg.SysUsePlayersDir? "Players/%s.mg%d" : "%s.mg%d", Players[Player_num].callsign, slot);
for (i = 0; i < N_players; i++)
multi_strip_robots(i);
if (multi_i_am_master()) // put all players to wait-state again so we can sync up properly
for (i = 0; i < MAX_PLAYERS; i++)
if (Players[i].connected == CONNECT_PLAYING && i != Player_num)
Players[i].connected = CONNECT_WAITING;
thisid=state_get_game_id(filename);
if (thisid!=id)
{
nm_messagebox(NULL, 1, TXT_OK, "A multi-save game was restored\nthat you are missing or does not\nmatch that of the others.\nYou must rejoin if you wish to\ncontinue.");
return;
}
pnum = state_restore_all_sub( filename );
}
// Following functions convert object to object_rw and back. Mainly this is used for IPX backwards compability. However also for UDP this makes sense as object differs from object_rw mainly between fix/fix64-based timers. Those base on GameTime64 which is never synced between players so we set the times to something sane the clients can safely handle. IF object some day contains something useful clients should know about this should be changed.
// turn object to object_rw for sending
void multi_object_to_object_rw(object *obj, object_rw *obj_rw)

View file

@ -104,13 +104,10 @@ extern int multi_protocol; // set and determinate used protocol
#define MULTI_BOSS_ACTIONS 30
#define MULTI_CREATE_ROBOT_POWERUPS 31
#define MULTI_HOSTAGE_DOOR 32
#define MULTI_SAVE_GAME 33 // obsolete
#define MULTI_RESTORE_GAME 34 // obsolete
#define MULTI_SAVE_GAME 33
#define MULTI_RESTORE_GAME 34
#define MULTI_REQ_PLAYER 35 // NEVER USED
#define MULTI_SEND_PLAYER 36 // NEVER USED
#define MULTI_POWCAP_UPDATE 37
#define MULTI_HEARTBEAT 38
#define MULTI_KILLGOALS 39
@ -227,6 +224,7 @@ void multi_send_message_dialog(void);
int multi_delete_extra_objects(void);
void multi_make_ghost_player(int objnum);
void multi_make_player_ghost(int objnum);
void multi_reset_player_object(object *objp);
void multi_define_macro(int key);
void multi_send_macro(int key);
int multi_get_kill_list(int *plist);
@ -236,6 +234,9 @@ void multi_reset_stuff(void);
void multi_send_data(unsigned char *buf, int len, int priority);
int get_team(int pnum);
int multi_maybe_disable_friendly_fire(object *killer);
void multi_initiate_save_game();
void multi_initiate_restore_game();
void multi_disconnect_player(int pnum);
void multi_object_to_object_rw(object *obj, object_rw *obj_rw);
void multi_object_rw_to_object(object_rw *obj_rw, object *obj);

View file

@ -91,7 +91,7 @@ extern int Lunacy;
extern void do_lunacy_on(void);
extern void do_lunacy_off(void);
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);
int sc_last_item= 0;
@ -562,19 +562,19 @@ int state_quick_item = -1;
int state_get_savegame_filename(char * fname, char * dsc, char * caption, int blind_save)
{
PHYSFS_file * fp;
int i, choice, version, nsaves;
int i, choice, version, dummy_state_game_id, nsaves;
newmenu_item m[NUM_SAVES+1];
char filename[NUM_SAVES][FILENAME_LEN + 9];
char filename[NUM_SAVES][PATH_MAX];
char desc[NUM_SAVES][DESC_LENGTH + 16];
grs_bitmap *sc_bmp[NUM_SAVES];
char id[5];
char id[5], dummy_callsign[CALLSIGN_LEN];
int valid;
nsaves=0;
m[0].type = NM_TYPE_TEXT; m[0].text = "\n\n\n\n";
for (i=0;i<NUM_SAVES; i++ ) {
sc_bmp[i] = NULL;
sprintf( filename[i], GameArg.SysUsePlayersDir? "Players/%s.sg%x" : "%s.sg%x", Players[Player_num].callsign, i );
sprintf( filename[i], GameArg.SysUsePlayersDir? "Players/%s.%sg%x" : "%s.%sg%x", Players[Player_num].callsign, (Game_mode & GM_MULTI_COOP)?"m":"s", i );
valid = 0;
fp = PHYSFSX_openReadBuffered(filename[i]);
if ( fp ) {
@ -583,6 +583,12 @@ int state_get_savegame_filename(char * fname, char * dsc, char * caption, int bl
if ( !memcmp( id, dgss_id, 4 )) {
//Read version
PHYSFS_read(fp, &version, sizeof(int), 1);
// In case it's Coop, read state_game_id & callsign as well
if (Game_mode & GM_MULTI_COOP)
{
dummy_state_game_id = PHYSFSX_readSXE32(fp, 0);
PHYSFS_read(fp, &dummy_callsign, sizeof(char)*CALLSIGN_LEN, 1);
}
if ((version >= 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; i<Num_walls; i++ ) {
if ( (Walls[i].segnum<0) || (Walls[i].segnum>Highest_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; i<Num_walls; i++ ) {
if ( (Walls[i].segnum<0) || (Walls[i].segnum>Highest_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;
}

View file

@ -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

View file

@ -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;