Beispiel #1
0
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);
	}
}
Beispiel #2
0
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 );
	}
}
Beispiel #3
0
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;
}
Beispiel #4
0
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();
}
Beispiel #5
0
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);
			}
		}
	}
}
Beispiel #6
0
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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
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);
}
Beispiel #9
0
void WriteInUseBits()
{
	ojk::SavedGameHelper saved_game(
		::gi.saved_game);

	saved_game.write_chunk<uint32_t>(
		INT_ID('I', 'N', 'U', 'S'),
		::g_entityInUseBits);
}
Beispiel #10
0
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();
}
Beispiel #12
0
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);
			}
		}
	}
}
Beispiel #13
0
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);
}
Beispiel #14
0
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;
		}
	}
}
Beispiel #15
0
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);
		}
	}
}
Beispiel #16
0
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 (...) {}
}
Beispiel #17
0
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")
	);

}
Beispiel #18
0
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";
}
Beispiel #19
0
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);
	}
}
Beispiel #20
0
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));
	}
}
Beispiel #21
0
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";
}
Beispiel #23
0
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
}
Beispiel #24
0
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;
}
Beispiel #25
0
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);
	}
Beispiel #26
0
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);
}
Beispiel #27
0
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...
	}
}
Beispiel #28
0
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;
}
Beispiel #29
0
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;
}