void G_SaveCachedRoffs() { int i, len; ojk::SavedGameHelper saved_game( ::gi.saved_game); // Write out the number of cached ROFFs saved_game.write_chunk<int32_t>( INT_ID('R', 'O', 'F', 'F'), ::num_roffs); // Now dump out the cached ROFF file names in order so they can be loaded on the other end for ( i = 0; i < num_roffs; i++ ) { // Dump out the string length to make things a bit easier on the other end...heh heh. len = strlen( roffs[i].fileName ) + 1; saved_game.write_chunk<int32_t>( INT_ID('S', 'L', 'E', 'N'), len); saved_game.write_chunk( INT_ID('R', 'S', 'T', 'R'), ::roffs[i].fileName, len); } }
void G_LoadCachedRoffs() { int i, count = 0, len = 0; char buffer[MAX_QPATH]; ojk::SavedGameHelper saved_game( ::gi.saved_game); // Get the count of goodies we need to revive saved_game.read_chunk<int32_t>( INT_ID('R', 'O', 'F', 'F'), count); // Now bring 'em back to life for ( i = 0; i < count; i++ ) { saved_game.read_chunk<int32_t>( INT_ID('S', 'L', 'E', 'N'), len); if (len < 0 || static_cast<size_t>(len) >= sizeof(buffer)) { len = 0; } saved_game.read_chunk( INT_ID('R', 'S', 'T', 'R'), buffer, len); G_LoadRoff( buffer ); } }
static char *GetStringPtr(int iStrlen, char *psOriginal/*may be NULL*/) { if (iStrlen != -1) { char sString[768]; // arb, inc if nec. sString[0]=0; assert(iStrlen+1<=(int)sizeof(sString)); ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.read_chunk( INT_ID('S', 'T', 'R', 'G'), sString, iStrlen); // TAG_G_ALLOC is always blown away, we can never recycle if (psOriginal && gi.bIsFromZone(psOriginal, TAG_G_ALLOC)) { if (!strcmp(psOriginal,sString)) {//it's a legal ptr and they're the same so let's just reuse it instead of free/alloc return psOriginal; } gi.Free(psOriginal); } return G_NewString(sString); } return NULL; }
void loadgame::load_multiplayer_game() { show_dialog(false, false); if (filename_.empty()) throw load_game_cancelled_exception(); std::string error_log; { cursor::setter cur(cursor::WAIT); log_scope("load_game"); read_save_file(filename_, load_config_, &error_log); copy_era(load_config_); gamestate_ = saved_game(load_config_); } if(!error_log.empty()) { gui2::show_error_message(gui_.video(), _("The file you have tried to load is corrupt: '") + error_log); throw load_game_cancelled_exception(); } if(gamestate_.classification().campaign_type != game_classification::MULTIPLAYER) { gui2::show_transient_error_message(gui_.video(), _("This is not a multiplayer save.")); throw load_game_cancelled_exception(); } check_version_compatibility(); }
void TIMER_Load( void ) { int j; gentity_t *ent; ojk::SavedGameHelper saved_game( ::gi.saved_game); for ( j = 0, ent = &g_entities[0]; j < MAX_GENTITIES; j++, ent++ ) { unsigned char numTimers = 0; saved_game.read_chunk<uint8_t>( INT_ID('T', 'I', 'M', 'E'), numTimers); //Read back all entries for ( int i = 0; i < numTimers; i++ ) { int time = 0; char tempBuffer[1024]; // Still ugly. Setting ourselves up for 007 AUF all over again. =) assert (sizeof(g_timers[0]->time) == sizeof(time) );//make sure we're reading the same size as we wrote //Read the id string and time saved_game.read_chunk( INT_ID('T', 'M', 'I', 'D')); const char* sg_buffer_data = static_cast<const char*>( saved_game.get_buffer_data()); int sg_buffer_size = saved_game.get_buffer_size(); if (sg_buffer_size < 0 || static_cast<size_t>(sg_buffer_size) >= sizeof(tempBuffer)) { sg_buffer_size = 0; } else { std::uninitialized_copy_n( sg_buffer_data, sg_buffer_size, tempBuffer); } tempBuffer[sg_buffer_size] = '\0'; saved_game.read_chunk<int32_t>( INT_ID('T', 'D', 'T', 'A'), time); //this is odd, we saved all the timers in the autosave, but not all the ents are spawned yet from an auto load, so skip it if (ent->inuse) { //Restore it TIMER_Set(ent, tempBuffer, time); } } } }
int CSequence::SaveCommand( CBlock *block ) { unsigned char flags; int numMembers, bID, size; CBlockMember *bm; ojk::SavedGameHelper saved_game( m_owner->GetInterface()->saved_game); //Save out the block ID bID = block->GetBlockID(); saved_game.write_chunk<int32_t>( INT_ID('B', 'L', 'I', 'D'), bID); //Save out the block's flags flags = block->GetFlags(); saved_game.write_chunk<uint8_t>( INT_ID('B', 'F', 'L', 'G'), flags); //Save out the number of members to read numMembers = block->GetNumMembers(); saved_game.write_chunk<int32_t>( INT_ID('B', 'N', 'U', 'M'), numMembers); for ( int i = 0; i < numMembers; i++ ) { bm = block->GetMember( i ); //Save the block id bID = bm->GetID(); saved_game.write_chunk<int32_t>( INT_ID('B', 'M', 'I', 'D'), bID); //Save out the data size size = bm->GetSize(); saved_game.write_chunk<int32_t>( INT_ID('B', 'S', 'I', 'Z'), size); //Save out the raw data const uint8_t* raw_data = static_cast<const uint8_t*>(bm->GetData()); saved_game.write_chunk( INT_ID('B', 'M', 'E', 'M'), raw_data, size); } return true; }
void level_to_gamestate(const config& level, saved_game& state) { game_classification::CAMPAIGN_TYPE type = state.classification().campaign_type; bool show_connect = state.mp_settings().show_connect; state = saved_game(level); state.classification().campaign_type = type; state.mp_settings().show_connect = show_connect; }
void G_LoadSave_ReadMiscData(void) { ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.read_chunk<int32_t>( INT_ID('L', 'C', 'K', 'D'), ::player_locked); }
void WriteInUseBits() { ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.write_chunk<uint32_t>( INT_ID('I', 'N', 'U', 'S'), ::g_entityInUseBits); }
wait::~wait() { try { if (get_result() == QUIT) { state_ = saved_game(); state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER; } } catch (...) {} }
create_engine::create_engine(game_display& disp, saved_game& state) : current_level_type_(), current_level_index_(0), current_era_index_(0), current_mod_index_(0), level_name_filter_(), player_count_filter_(1), scenarios_(), user_maps_(), user_scenarios_(), campaigns_(), sp_campaigns_(), random_maps_(), user_map_names_(), user_scenario_names_(), eras_(), mods_(), state_(state), dependency_manager_(resources::config_manager->game_config(), disp.video()), generator_(NULL) { DBG_MP << "restoring game config\n"; // Restore game config for multiplayer. state_ = saved_game(); state_.classification().campaign_type = game_classification::MULTIPLAYER; resources::config_manager-> load_game_config_for_game(state_.classification()); //TODO the editor dir is already configurable, is the preferences value get_files_in_dir(get_user_data_dir() + "/editor/maps", &user_map_names_, NULL, FILE_NAME_ONLY); get_files_in_dir(get_user_data_dir() + "/editor/scenarios", &user_scenario_names_, NULL, FILE_NAME_ONLY); DBG_MP << "initializing all levels, eras and mods\n"; init_all_levels(); init_extras(ERA); init_extras(MOD); state_.mp_settings().saved_game = false; BOOST_FOREACH (const std::string& str, preferences::modifications()) { if (resources::config_manager-> game_config().find_child("modification", "id", str)) state_.mp_settings().active_mods.push_back(str); } if (current_level_type_ != level::CAMPAIGN && current_level_type_ != level::SP_CAMPAIGN) { dependency_manager_.try_modifications(state_.mp_settings().active_mods, true); } reset_level_filters(); }
void TIMER_Load( void ) { int j; gentity_t *ent; ojk::SavedGameHelper saved_game( ::gi.saved_game); for ( j = 0, ent = &g_entities[0]; j < MAX_GENTITIES; j++, ent++ ) { int numTimers = 0; saved_game.read_chunk<int32_t>( INT_ID('T', 'I', 'M', 'E'), numTimers); //Make sure there's something to read if ( numTimers == 0 ) continue; //Read back all entries for ( int i = 0; i < numTimers; i++ ) { int length = 0, time = 0; char tempBuffer[1024]; // Still ugly. Setting ourselves up for 007 AUF all over again. =) assert (sizeof(g_timers[0]->time) == sizeof(time) );//make sure we're reading the same size as we wrote saved_game.read_chunk<int32_t>( INT_ID('T', 'S', 'L', 'N'), length); if ( length >= 1024 ) { assert( 0 ); continue; } //Read the id and time saved_game.read_chunk( INT_ID('T', 'S', 'N', 'M'), tempBuffer, length); tempBuffer[length] = '\0'; saved_game.read_chunk<int32_t>( INT_ID('T', 'D', 'T', 'A'), time); //this is odd, we saved all the timers in the autosave, but not all the ents are spawned yet from an auto load, so skip it if (ent->inuse) { //Restore it TIMER_Set(ent, tempBuffer, time); } } } }
bool game_launcher::new_campaign() { state_ = saved_game(); state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::SCENARIO; state_.mp_settings().show_connect = false; play_replay_ = false; return sp::enter_create_mode(video(), game_config_manager::get()->game_config(), state_, jump_to_campaign_, true); }
void TIMER_Save( void ) { int j; gentity_t *ent; ojk::SavedGameHelper saved_game( ::gi.saved_game); for ( j = 0, ent = &g_entities[0]; j < MAX_GENTITIES; j++, ent++ ) { int numTimers = TIMER_GetCount(j); if ( !ent->inuse && numTimers) { // Com_Printf( "WARNING: ent with timers not inuse\n" ); assert(numTimers); TIMER_Clear( j ); numTimers = 0; } //Write out the timer information saved_game.write_chunk<int32_t>( INT_ID('T', 'I', 'M', 'E'), numTimers); gtimer_t *p = g_timers[j]; assert ((numTimers && p) || (!numTimers && !p)); while(p) { const char *timerID = p->id.c_str(); const int length = strlen(timerID) + 1; const int time = p->time - level.time; //convert this back to delta so we can use SET after loading assert( length < 1024 );//This will cause problems when loading the timer if longer //Write out the string size and data saved_game.write_chunk<int32_t>( INT_ID('T', 'S', 'L', 'N'), length); saved_game.write_chunk( INT_ID('T', 'S', 'N', 'M'), timerID, length); //Write out the timer data saved_game.write_chunk<int32_t>( INT_ID('T', 'D', 'T', 'A'), time); p = p->next; } } }
static void EvaluateFields( const save_field_t* pFields, T* pbData, byte* pbOriginalRefData, unsigned int ulChid) { T& instance = *pbData; ojk::SavedGameHelper saved_game( ::gi.saved_game); if (ulChid != INT_ID('G', 'C', 'L', 'I')) { saved_game.read_chunk( ulChid, instance); } else { if (!saved_game.try_read_chunk( ulChid, instance)) { RetailGClient retail_client; saved_game.reset_buffer_offset(); if (saved_game.try_read( retail_client)) { copy_retail_gclient_to_current( retail_client, *reinterpret_cast<gclient_t*>(pbData)); } else { ::G_Error( ::va("EvaluateFields(): variable-sized chunk '%s' without handler!", ::SG_GetChidText(ulChid))); } } } if (pFields) { for (const save_field_t* pField = pFields; pField->psName; ++pField) { ::EvaluateField( pField, reinterpret_cast<byte*>(pbData), pbOriginalRefData); } } }
wait::~wait() { try { if (get_result() == QUIT) { state_ = saved_game(); state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER; game_config_manager::get()-> load_game_config_for_game(state_.classification()); } } catch (...) {} }
void game_launcher::set_tutorial() { state_ = saved_game(); state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::TUTORIAL; state_.classification().campaign_define = "TUTORIAL"; state_.mp_settings().mp_era = "era_default"; state_.mp_settings().show_connect = false; state_.set_carryover_sides_start( config_of("next_scenario", "tutorial") ); }
void playsingle_controller::linger() { LOG_NG << "beginning end-of-scenario linger\n"; browse_ = true; linger_ = true; // If we need to set the status depending on the completion state // the key to it is here. gui_->set_game_mode(game_display::LINGER_SP); // this is actually for after linger mode is over -- we don't // want to stay stuck in linger state when the *next* scenario // is over. set_completion setter(saved_game_,"running"); // change the end-turn button text to its alternate label gui_->get_theme().refresh_title2("button-endturn", "title2"); gui_->invalidate_theme(); gui_->redraw_everything(); // End all unit moves gamestate_.board_.set_all_units_user_end_turn(); try { // Same logic as single-player human turn, but // *not* the same as multiplayer human turn. end_turn_enable(true); end_turn_ = false; while(!end_turn_) { // Reset the team number to make sure we're the right team. player_number_ = first_player_; play_slice(); gui_->draw(); } } catch(const game::load_game_exception &) { // Loading a new game is effectively a quit. if ( game::load_game_exception::game != "" ) { saved_game_ = saved_game(); } throw; } // revert the end-turn button text to its normal label gui_->get_theme().refresh_title2("button-endturn", "title"); gui_->invalidate_theme(); gui_->redraw_everything(); gui_->set_game_mode(game_display::RUNNING); LOG_NG << "ending end-of-scenario linger\n"; }
void ReadInUseBits() { ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.read_chunk<uint32_t>( INT_ID('I', 'N', 'U', 'S'), ::g_entityInUseBits); // This is only temporary. Once I have converted all the ent->inuse refs, // it won;t be needed -MW. for(int i=0;i<MAX_GENTITIES;i++) { g_entities[i].inuse=PInUse(i); } }
static void EnumerateFields( const save_field_t* pFields, const T* src_instance, unsigned int ulChid) { strList.clear(); const byte* pbData = reinterpret_cast<const byte*>( src_instance); // enumerate all the fields... // if (pFields) { for (auto pField = pFields; pField->psName; ++pField) { assert(pField->iOffset < sizeof(T)); ::EnumerateField(pField, pbData); } } ojk::SavedGameHelper saved_game( ::gi.saved_game); // save out raw data... // saved_game.reset_buffer(); src_instance->sg_export( saved_game); saved_game.write_chunk( ulChid); // save out any associated strings.. // for (const auto& it : strList) { saved_game.write_chunk( INT_ID('S', 'T', 'R', 'G'), it.c_str(), static_cast<int>(it.length() + 1)); } }
void level_to_gamestate(const config& level, saved_game& state) { game_classification::CAMPAIGN_TYPE type = state.classification().campaign_type; state = saved_game(level); state.classification().campaign_type = type; // Any replay data is only temporary and should be removed from // the level data in case we want to save the game later. if (const config& replay_data = level.child("replay")) { LOG_NW << "setting replay\n"; recorder = replay(replay_data); if (!recorder.empty()) { recorder.set_skip(false); recorder.set_to_end(); } } //save id setting was moved to play_controller. }
void playsingle_controller::linger() { LOG_NG << "beginning end-of-scenario linger\n"; linger_ = true; // If we need to set the status depending on the completion state // the key to it is here. gui_->set_game_mode(game_display::LINGER); // change the end-turn button text to its alternate label gui_->get_theme().refresh_title2("button-endturn", "title2"); gui_->invalidate_theme(); gui_->redraw_everything(); // End all unit moves gamestate().board_.set_all_units_user_end_turn(); try { // Same logic as single-player human turn, but // *not* the same as multiplayer human turn. end_turn_enable(true); end_turn_ = END_TURN_NONE; while(end_turn_ == END_TURN_NONE) { play_slice(); gui_->draw(); } } catch(const game::load_game_exception &) { // Loading a new game is effectively a quit. if ( game::load_game_exception::game != "" ) { saved_game_ = saved_game(); } throw; } // revert the end-turn button text to its normal label gui_->get_theme().refresh_title2("button-endturn", "title"); gui_->invalidate_theme(); gui_->redraw_everything(); gui_->set_game_mode(game_display::RUNNING); LOG_NG << "ending end-of-scenario linger\n"; }
void loadgame::set_gamestate() { gamestate_ = saved_game(load_config_); #if 0 //we dont need this code since we always restore our random from [snapshot] or [replay_start] (except for start of scenario saves where we don't have those) //also the random_seed isn't stored at toplevel anymore. // Get the status of the random in the snapshot. // For a replay we need to restore the start only, the replaying gets at // proper location. // For normal loading also restore the call count. int seed = load_config_["random_seed"].to_int(42); if(seed == 42){ config cfg = load_config_.child_or_empty("carryover_sides_start"); seed = cfg["random_seed"].to_int(42); } unsigned calls = show_replay_ ? 0 : gamestate_.snapshot["random_calls"].to_int(); carryover_info sides(gamestate_.carryover_sides_start); sides.rng().seed_random(seed, calls); gamestate_.carryover_sides_start = sides.to_config(); #endif }
bool game_launcher::play_multiplayer_commandline() { if(!cmdline_opts_.multiplayer) { return true; } DBG_MP << "starting multiplayer game from the commandline" << std::endl; // These are all the relevant lines taken literally from play_multiplayer() above state_ = saved_game(); state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER; game_config_manager::get()-> load_game_config_for_game(state_.classification()); events::discard_input(); // prevent the "keylogger" effect cursor::set(cursor::NORMAL); mp::start_local_game_commandline(video(), game_config_manager::get()->game_config(), state_, cmdline_opts_); return false; }
int CSequence::Save( void ) { sequence_l::iterator ci; block_l::iterator bi; int id; ojk::SavedGameHelper saved_game( m_owner->GetInterface()->saved_game); //Save the parent (by GUID) id = ( m_parent != NULL ) ? m_parent->GetID() : -1; saved_game.write_chunk<int32_t>( INT_ID('S', 'P', 'I', 'D'), id); //Save the return (by GUID) id = ( m_return != NULL ) ? m_return->GetID() : -1; saved_game.write_chunk<int32_t>( INT_ID('S', 'R', 'I', 'D'), id); //Save the number of children saved_game.write_chunk<int32_t>( INT_ID('S', 'N', 'C', 'H'), m_numChildren); //Save out the children (only by GUID) STL_ITERATE( ci, m_children ) { id = (*ci)->GetID(); saved_game.write_chunk<int32_t>( INT_ID('S', 'C', 'H', 'D'), id); }
void WriteLevel(qboolean qbAutosave) { if (!qbAutosave) //-always save the client { // write out one client - us! // assert(level.maxclients == 1); // I'll need to know if this changes, otherwise I'll need to change the way ReadGame works gclient_t client = level.clients[0]; EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I')); WriteLevelLocals(); // level_locals_t level } OBJ_SaveObjectiveData(); FX_Write(); ///////////// WriteGEntities(qbAutosave); Quake3Game()->VariableSave(); G_LoadSave_WriteMiscData(); extern void CG_WriteTheEvilCGHackStuff(void); CG_WriteTheEvilCGHackStuff(); // (Do NOT put any write-code below this line) // // put out an end-marker so that the load code can check everything was read in... // static int iDONE = 1234; ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.write_chunk<int32_t>( INT_ID('D', 'O', 'N', 'E'), iDONE); }
static void ReadGEntities(qboolean qbAutosave) { int iCount = 0; int i; ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.read_chunk<int32_t>( INT_ID('N', 'M', 'E', 'D'), iCount); int iPreviousEntRead = -1; for (i=0; i<iCount; i++) { int iEntIndex = 0; saved_game.read_chunk<int32_t>( INT_ID('E', 'D', 'N', 'M'), iEntIndex); if (iEntIndex >= globals.num_entities) { globals.num_entities = iEntIndex + 1; } if (iPreviousEntRead != iEntIndex-1) { for (int j=iPreviousEntRead+1; j!=iEntIndex; j++) { if ( g_entities[j].inuse ) // not actually necessary { G_FreeEntity(&g_entities[j]); } } } iPreviousEntRead = iEntIndex; // slightly naff syntax here, but makes a few ops clearer later... // gentity_t entity; gentity_t* pEntOriginal = &entity; gentity_t* pEnt = &g_entities[iEntIndex]; *pEntOriginal = *pEnt; // struct copy, so we can refer to original pEntOriginal->ghoul2.kill(); gi.unlinkentity(pEnt); Quake3Game()->FreeEntity( pEnt ); // // sneaky: destroy the ghoul2 object within this struct before binary-loading over the top of it... // gi.G2API_LoadSaveCodeDestructGhoul2Info(pEnt->ghoul2); pEnt->ghoul2.kill(); EvaluateFields(savefields_gEntity, pEnt, (byte *)pEntOriginal, INT_ID('G','E','N','T')); pEnt->ghoul2.kill(); // now for any fiddly bits... // if (pEnt->NPC) // will be qtrue/qfalse { gNPC_t tempNPC; EvaluateFields(savefields_gNPC, &tempNPC,(byte *)pEntOriginal->NPC, INT_ID('G','N','P','C')); // so can we pinch the original's one or do we have to alloc a new one?... // if (pEntOriginal->NPC) { // pinch this G_Alloc handle... // pEnt->NPC = pEntOriginal->NPC; } else { // original didn't have one (hmmm...), so make a new one... // //assert(0); // I want to know about this, though not in release pEnt->NPC = (gNPC_t *) G_Alloc(sizeof(*pEnt->NPC)); } // copy over the one we've just loaded... // *pEnt->NPC = tempNPC; // struct copy //FIXME: do we need to do these too? /* if ( pEnt->s.number ) {//not player G_LoadAnimFileSet( *pEnt, *pEnt->NPC_type ); G_SetSkin( *pEnt, *pEnt->NPC_type, NULL );// it probably wasn't the default skin, do we need this at all? } */ } if (pEnt->client == (gclient_t*) -2) // one of Mike G's NPC clients? { gclient_t tempGClient; EvaluateFields(savefields_gClient, &tempGClient, (byte *)pEntOriginal->client, INT_ID('G','C','L','I')); // can we pinch the original's client handle or do we have to alloc a new one?... // if (pEntOriginal->client) { // pinch this G_Alloc handle... // pEnt->client = pEntOriginal->client; } else { // original didn't have one (hmmm...) so make a new one... // pEnt->client = (gclient_t *) G_Alloc(sizeof(*pEnt->client)); } // copy over the one we've just loaded.... // *pEnt->client = tempGClient; // struct copy if ( pEnt->s.number ) {//not player G_ReloadSaberData( pEnt ); } } // Some Icarus thing... (probably) // if (pEnt->parms) // will be qtrue/qfalse { parms_t tempParms; saved_game.read_chunk( INT_ID('P', 'A', 'R', 'M'), tempParms); // so can we pinch the original's one or do we have to alloc a new one?... // if (pEntOriginal->parms) { // pinch this G_Alloc handle... // pEnt->parms = pEntOriginal->parms; } else { // original didn't have one, so make a new one... // pEnt->parms = (parms_t *) G_Alloc(sizeof(*pEnt->parms)); } // copy over the one we've just loaded... // *pEnt->parms = tempParms; // struct copy } if (pEnt->m_pVehicle) // will be qtrue/qfalse { Vehicle_t tempVehicle; EvaluateFields(savefields_gVHIC, &tempVehicle,(byte *)pEntOriginal->m_pVehicle, INT_ID('V','H','I','C')); // so can we pinch the original's one or do we have to alloc a new one?... // if (pEntOriginal->m_pVehicle) { // pinch this G_Alloc handle... // pEnt->m_pVehicle = pEntOriginal->m_pVehicle; } else { // original didn't have one, so make a new one... // pEnt->m_pVehicle = (Vehicle_t *) gi.Malloc( sizeof(Vehicle_t), TAG_G_ALLOC, qfalse ); } // copy over the one we've just loaded... // *pEnt->m_pVehicle = tempVehicle; // struct copy } // the scary ghoul2 stuff... (fingers crossed) // { saved_game.read_chunk( INT_ID('G', 'H', 'L', '2')); gi.G2API_LoadGhoul2Models(pEnt->ghoul2, nullptr); } // gi.unlinkentity (pEntOriginal); // ICARUS_FreeEnt( pEntOriginal ); // *pEntOriginal = *pEnt; // struct copy // qboolean qbLinked = pEntOriginal->linked; // pEntOriginal->linked = qfalse; // if (qbLinked) // { // gi.linkentity (pEntOriginal); // } // because the sytem stores sfx_t handles directly instead of the set, we have to reget the set's sfx_t... // if (pEnt->s.eType == ET_MOVER && pEnt->s.loopSound>0) { if ( VALIDSTRING( pEnt->soundSet )) { extern int BMS_MID; // from g_mover pEnt->s.loopSound = CAS_GetBModelSound( pEnt->soundSet, BMS_MID ); if (pEnt->s.loopSound == -1) { pEnt->s.loopSound = 0; } } } // NPCs and other ents store waypoints that aren't valid after a load pEnt->waypoint = 0; qboolean qbLinked = pEnt->linked; pEnt->linked = qfalse; if (qbLinked) { gi.linkentity (pEnt); } } //Read in all the entity timers TIMER_Load();//ReadEntityTimers(); if (!qbAutosave) { // now zap any g_ents that were inuse when the level was loaded, but are no longer in use in the saved version // that we've just loaded... // for (i=iPreviousEntRead+1; i<globals.num_entities; i++) { if ( g_entities[i].inuse ) // not actually necessary { G_FreeEntity(&g_entities[i]); } } //Load ICARUS information Quake3Game()->ClearEntityList(); IIcarusInterface::GetIcarus()->Load(); // check that Icarus has loaded everything it saved out by having a marker chunk after it... // static int iBlah = 1234; saved_game.read_chunk<int32_t>( INT_ID('I', 'C', 'O', 'K'), iBlah); } if (!qbAutosave) { ReadInUseBits();//really shouldn't need to read these bits in at all, just restore them from the ents... } }
LEVEL_RESULT playsingle_controller::play_scenario( const config::const_child_itors &story, bool skip_replay) { LOG_NG << "in playsingle_controller::play_scenario()...\n"; // Start music. BOOST_FOREACH(const config &m, level_.child_range("music")) { sound::play_music_config(m); } sound::commit_music_changes(); if(!skip_replay) { show_story(*gui_, level_["name"], story); } gui_->labels().read(level_); // Read sound sources assert(soundsources_manager_ != NULL); BOOST_FOREACH(const config &s, level_.child_range("sound_source")) { try { soundsource::sourcespec spec(s); soundsources_manager_->add(spec); } catch (bad_lexical_cast &) { ERR_NG << "Error when parsing sound_source config: bad lexical cast." << std::endl; ERR_NG << "sound_source config was: " << s.debug() << std::endl; ERR_NG << "Skipping this sound source..." << std::endl; } } set_victory_when_enemies_defeated(level_["victory_when_enemies_defeated"].to_bool(true)); set_remove_from_carryover_on_defeat(level_["remove_from_carryover_on_defeat"].to_bool(true)); end_level_data &end_level = get_end_level_data(); end_level.carryover_percentage = level_["carryover_percentage"].to_int(game_config::gold_carryover_percentage); end_level.carryover_add = level_["carryover_add"].to_bool(); bool past_prestart = false; LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n"; try { possible_end_play_signal signal = play_scenario_init(end_level, past_prestart); if (!signal) { signal = play_scenario_main_loop(end_level, past_prestart); } if (signal) { switch (boost::apply_visitor( get_signal_type(), *signal )) { //BEGIN CASES case END_TURN: assert(false && "end turn signal propogated to playsingle_controller::play_scenario. This results in terminate!"); throw 42; case END_LEVEL: if(!past_prestart) { sdl::draw_solid_tinted_rectangle( 0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0, gui_->video().getSurface() ); update_rect(0, 0, gui_->video().getx(), gui_->video().gety()); } ai_testing::log_game_end(); LEVEL_RESULT end_level_result = boost::apply_visitor( get_result(), *signal ); if (!end_level.transient.custom_endlevel_music.empty()) { if (end_level_result == DEFEAT) { set_defeat_music_list(end_level.transient.custom_endlevel_music); } else { set_victory_music_list(end_level.transient.custom_endlevel_music); } } if (gamestate_.board_.teams().empty()) { //store persistent teams saved_game_.set_snapshot(config()); return VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event } const bool obs = is_observer(); if (game_config::exit_at_end) { exit(0); } if (end_level_result == DEFEAT || end_level_result == VICTORY) { saved_game_.classification().completion = (end_level_result == VICTORY) ? "victory" : "defeat"; // If we're a player, and the result is victory/defeat, then send // a message to notify the server of the reason for the game ending. if (!obs) { config cfg; config& info = cfg.add_child("info"); info["type"] = "termination"; info["condition"] = "game over"; info["result"] = saved_game_.classification().completion; network::send_data(cfg, 0); } else { gui2::show_transient_message(gui_->video(),_("Game Over"), _("The game is over.")); return OBSERVER_END; } } if (end_level_result == QUIT) { return QUIT; } else if (end_level_result == DEFEAT) { saved_game_.classification().completion = "defeat"; game_events::fire("defeat"); if (!obs) { const std::string& defeat_music = select_defeat_music(); if(defeat_music.empty() != true) sound::play_music_once(defeat_music); persist_.end_transaction(); return DEFEAT; } else { return QUIT; } } else if (end_level_result == VICTORY) { saved_game_.classification().completion = !end_level.transient.linger_mode ? "running" : "victory"; game_events::fire("victory"); // // Play victory music once all victory events // are finished, if we aren't observers. // // Some scenario authors may use 'continue' // result for something that is not story-wise // a victory, so let them use [music] tags // instead should they want special music. // if (!obs && end_level.transient.linger_mode) { const std::string& victory_music = select_victory_music(); if(victory_music.empty() != true) sound::play_music_once(victory_music); } // Add all the units that survived the scenario. // this function doesn't move unit to the recalllist anymore i just keep this name to prevent merging conflicts. LOG_NG << "Add units that survived the scenario to the recall list.\n"; gamestate_.board_.all_survivors_to_recall(); saved_game_.remove_snapshot(); if(!is_observer()) { persist_.end_transaction(); } return VICTORY; } else if (end_level_result == SKIP_TO_LINGER) { LOG_NG << "resuming from loaded linger state...\n"; //as carryover information is stored in the snapshot, we have to re-store it after loading a linger state saved_game_.set_snapshot(config()); if(!is_observer()) { persist_.end_transaction(); } return VICTORY; } break; //END CASES } // END SWITCH } //end if } catch(const game::load_game_exception &) { // Loading a new game is effectively a quit. // if ( game::load_game_exception::game != "" ) { saved_game_ = saved_game(); } throw; } catch(network::error& e) { bool disconnect = false; if(e.socket) { e.disconnect(); disconnect = true; } savegame::ingame_savegame save(saved_game_, *gui_, to_config(), preferences::save_compression_format()); save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO); if(disconnect) { throw network::error(); } else { return QUIT; } } return QUIT; }
static void WriteGEntities(qboolean qbAutosave) { int iCount = 0; int i; for (i=0; i<(qbAutosave?1:globals.num_entities); i++) { gentity_t* ent = &g_entities[i]; if ( ent->inuse ) { iCount++; } } ojk::SavedGameHelper saved_game( ::gi.saved_game); saved_game.write_chunk<int32_t>( INT_ID('N', 'M', 'E', 'D'), iCount); for (i=0; i<(qbAutosave?1:globals.num_entities); i++) { gentity_t* ent = &g_entities[i]; if ( ent->inuse) { saved_game.write_chunk<int32_t>( INT_ID('E', 'D', 'N', 'M'), i); qboolean qbLinked = ent->linked; gi.unlinkentity( ent ); gentity_t tempEnt = *ent; // make local copy tempEnt.linked = qbLinked; if (qbLinked) { gi.linkentity( ent ); } EnumerateFields(savefields_gEntity, &tempEnt, INT_ID('G','E','N','T')); // now for any fiddly bits that would be rather awkward to build into the enumerator... // if (tempEnt.NPC) { gNPC_t npc = *ent->NPC; // NOT *tempEnt.NPC; !! :-) EnumerateFields(savefields_gNPC, &npc, INT_ID('G','N','P','C')); } if (tempEnt.client == (gclient_t *)-2) // I know, I know... { gclient_t client = *ent->client; // NOT *tempEnt.client!! EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I')); } if (tempEnt.parms) { saved_game.write_chunk( INT_ID('P', 'A', 'R', 'M'), *ent->parms); } if (tempEnt.m_pVehicle) { Vehicle_t vehicle = *ent->m_pVehicle; // NOT *tempEnt.m_pVehicle!! EnumerateFields(savefields_gVHIC, &vehicle, INT_ID('V','H','I','C')); } // the scary ghoul2 saver stuff... (fingers crossed) // gi.G2API_SaveGhoul2Models(tempEnt.ghoul2); tempEnt.ghoul2.kill(); // this handle was shallow copied from an ent. We don't want it destroyed } } //Write out all entity timers TIMER_Save();//WriteEntityTimers(); if (!qbAutosave) { //Save out ICARUS information IIcarusInterface::GetIcarus()->Save(); // this marker needs to be here, it lets me know if Icarus doesn't load everything back later, // which has happened, and doesn't always show up onscreen until certain game situations. // This saves time debugging, and makes things easier to track. // static int iBlah = 1234; saved_game.write_chunk<int32_t>( INT_ID('I', 'C', 'O', 'K'), iBlah); } if (!qbAutosave )//really shouldn't need to write these bits at all, just restore them from the ents... { WriteInUseBits(); } }
LEVEL_RESULT playsingle_controller::play_scenario(const config& level) { LOG_NG << "in playsingle_controller::play_scenario()...\n"; // Start music. BOOST_FOREACH(const config &m, level.child_range("music")) { sound::play_music_config(m); } sound::commit_music_changes(); if(!this->is_skipping_replay()) { show_story(gui_->video(), get_scenario_name(), level.child_range("story")); } gui_->labels().read(level); // Read sound sources assert(soundsources_manager_ != NULL); BOOST_FOREACH(const config &s, level.child_range("sound_source")) { try { soundsource::sourcespec spec(s); soundsources_manager_->add(spec); } catch (bad_lexical_cast &) { ERR_NG << "Error when parsing sound_source config: bad lexical cast." << std::endl; ERR_NG << "sound_source config was: " << s.debug() << std::endl; ERR_NG << "Skipping this sound source..." << std::endl; } } LOG_NG << "entering try... " << (SDL_GetTicks() - ticks()) << "\n"; try { play_scenario_init(); // clears level config; this->saved_game_.remove_snapshot(); if (!is_regular_game_end() && !linger_) { play_scenario_main_loop(); } if (game_config::exit_at_end) { exit(0); } const bool is_victory = get_end_level_data_const().is_victory; if(gamestate().gamedata_.phase() <= game_data::PRESTART) { sdl::draw_solid_tinted_rectangle( 0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0, gui_->video().getSurface() ); update_rect(0, 0, gui_->video().getx(), gui_->video().gety()); } ai_testing::log_game_end(); const end_level_data& end_level = get_end_level_data_const(); if (!end_level.transient.custom_endlevel_music.empty()) { if (!is_victory) { set_defeat_music_list(end_level.transient.custom_endlevel_music); } else { set_victory_music_list(end_level.transient.custom_endlevel_music); } } if (gamestate().board_.teams().empty()) { //store persistent teams saved_game_.set_snapshot(config()); return LEVEL_RESULT::VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event } if(linger_) { LOG_NG << "resuming from loaded linger state...\n"; //as carryover information is stored in the snapshot, we have to re-store it after loading a linger state saved_game_.set_snapshot(config()); if(!is_observer()) { persist_.end_transaction(); } return LEVEL_RESULT::VICTORY; } pump().fire(is_victory ? "victory" : "defeat"); { // Block for set_scontext_synced_base set_scontext_synced_base sync; pump().fire("scenario end"); } if(end_level.proceed_to_next_level) { gamestate().board_.heal_all_survivors(); } if(is_observer()) { gui2::show_transient_message(gui_->video(), _("Game Over"), _("The game is over.")); return LEVEL_RESULT::OBSERVER_END; } // If we're a player, and the result is victory/defeat, then send // a message to notify the server of the reason for the game ending. network::send_data(config_of ("info", config_of ("type", "termination") ("condition", "game over") ("result", is_victory ? "victory" : "defeat") )); // Play victory music once all victory events // are finished, if we aren't observers. // // Some scenario authors may use 'continue' // result for something that is not story-wise // a victory, so let them use [music] tags // instead should they want special music. const std::string& end_music = is_victory ? select_victory_music() : select_defeat_music(); if(end_music.empty() != true) { sound::play_music_once(end_music); } persist_.end_transaction(); return is_victory ? LEVEL_RESULT::VICTORY : LEVEL_RESULT::DEFEAT; } catch(const game::load_game_exception &) { // Loading a new game is effectively a quit. // if ( game::load_game_exception::game != "" ) { saved_game_ = saved_game(); } throw; } catch(network::error& e) { bool disconnect = false; if(e.socket) { e.disconnect(); disconnect = true; } scoped_savegame_snapshot snapshot(*this); savegame::ingame_savegame save(saved_game_, *gui_, preferences::save_compression_format()); save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO); if(disconnect) { throw network::error(); } else { return LEVEL_RESULT::QUIT; } } return LEVEL_RESULT::QUIT; }