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:
parent
2232433860
commit
eb0d876b9c
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
341
main/multi.c
341
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<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)
|
||||
|
|
11
main/multi.h
11
main/multi.h
|
@ -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);
|
||||
|
||||
|
|
690
main/state.c
690
main/state.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue