All pastes #839707 Raw Edit

Untitled

public text v1 · immutable
#839707 ·published 2007-12-31 05:00 UTC
rendered paste body
//
//	MegaTF Coop: Last Man Standing
//		by XavioR
//

// LMS: The player(s) are spawned in an area on a map and have X number of lives.
// The objective is to defend against waves of increasingly difficult monsters while
// not losing all lives.
//	Map requirements:
// 1) "info_lmsdetect" : tells the game we're playing LMS and precache's all monsters - also sets up lives over "info_tfdetect"
// 2) "func_monsterspawn" : entities from which monsters will spawn - monsters will then move towards the "trigger" entity
// 3) (Optional:) <mapname>.tfi - specifies all LMS map info inside a .tfi (see lms_keep.tfi for more info)

float last_level;

#define LMS_PREMATCH_TIME	20		// amount of time players have before the first wave of monsters
#define LMS_MATCH_TIME		60		// how long players have until the next wave spawns.
#define LMS_LAST_LEVEL		last_level
#define LMS_REST_TIME		15		// rest time in between levels

#define spawnfunc 		th_fireball
#define spawn_starttime		speed_time
#define spawn_active		speed_finished
#define spawn_tries		monsterflags
#define lms_starttime		monster_items
#define lms_level			lives
#define lms_startteamlives	ammo_shells
//#define lms_game_begun	health_attr			// defs.pqc'd
//#define lms_useroundlives	armor_attr			//  ""
//#define lms_usegloballives	damage_attr			//  ""
//#define lms_lives		is_malfunctioning		//  ""
#define lms_replenish_lives	monster_items
#define lms_startmatch_numplayers	lms_entity.misc_attr
#define lms_override_speed	super_damage_finished

#ifdef mtf_coop_expsys
// read data from .tfi instead of using hard-coded stuff. This needs coop_rankings, because onlt servers that are coop_rankings-compatable
//can use extensions like fopen/close/etc
#define lms_readfile 		reload_attr
#endif

void () lms_goalie_think;
void () spawnwave;
void ( entity newmon, float m_level ) GetMonsterSpawn;
void ( entity newmon, string newmon_cln ) SetSpawnFunc;

void ( entity m_precache ) SetupMonsterPrecache =
{
	local entity oself;
	oself = self;
	self = m_precache;
	m_precache.#spawnfunc ();
	m_precache.nextthink = time + 666;
	self = oself;
};

void () pre_spawnwave =
{
// XavioR: TODO: eh, this is flawed - what happens when players join/leave?
	bprint( 2, "Last Man Standing is beginning! Setting team lives to: " );
	bprint( 2, ftos( self.#lms_startteamlives/* + ceil( (#lms_startmatch_numplayers - 1) * 1.25) */) );
	bprint( 2, "\n" );

	if (self.#lms_useroundlives > 0) {
		bprint( 2, "Player lives (per round): " );
		bprint( 2, ftos( self.#lms_useroundlives ) );
		bprint( 2, "\n" );
	}
	else {
		bprint( 2, "Player lives (global): " );
		bprint( 2, ftos( self.#lms_usegloballives ) );
		bprint( 2, "\n" );
	}

	team1lives = self.#lms_startteamlives;// + ceil( (#lms_startmatch_numplayers - 1) * 1.25);

	newmis = spawn ();	// new entity to keep track of player lives (game goalie)
	newmis.#lms_startteamlives = self.#lms_startteamlives;
	newmis.think = lms_goalie_think;
	newmis.classname = "lms_goalie";
	newmis.nextthink = time + 5;

	self.think = spawnwave;
	self.#lms_game_begun = #TRUE;		// now players can't join if #lms_usegloballives > 0
	spawnwave();
};

void () wait_for_players =
{
	if (num_players > 0) {
		bprint( 2, "Last Man Standing: Waiting for all players to join (" );
		bprint( 2, ftos( #LMS_PREMATCH_TIME ) );
		bprint( 2, " seconds)\n" );
		self.nextthink = time + #LMS_PREMATCH_TIME;
		self.think = pre_spawnwave;
	}
	else {
		self.think = wait_for_players;
		self.nextthink = time + 1;
	}
}

void () info_lmsdetect =
{
	local entity m_precache;

	#LMS_LAST_LEVEL = 14;
#ifdef mtf_coop_expsys
	local string fname, a;
	local float file, default_spawn;

	fname = frik_strcat( "maps/", mapname, ".tfi" );
	file = open (fname, 0);

	if ( file == -1 )
	{
		bprint( 2, "No .tfi for " );
		bprint( 2, mapname );
		bprint( 2, " - using default monster info\n" );
		self.#lms_readfile = #FALSE;
	}
	else {
		self.#lms_readfile = #TRUE;

		// Get map rules
		a = "";
		do
		{
			a = read (file);
			if ( a == "//END" )
			{
				bprint (2, "info_lmsdetect: Game rules not found in .tfi - fix your files!!\n");
				self.#lms_readfile = #FALSE;
				break;
			}
			if ( a == "// GAME RULES" )
			{
				a = read (file);
				a = read (file);
				self.#lms_startteamlives = stof(a);
				a = read (file);
				a = read (file);
				self.#lms_useroundlives = stof(a);
				a = read (file);
				a = read (file);
				self.#lms_usegloballives = stof(a);
				a = read (file);
				a = read (file);
				#LMS_LAST_LEVEL = stof(a);
				a = read (file);
				a = read (file);
				if (a == "default")
					default_spawn = #TRUE;
				break;
			}
	
		} while (1);

		close (file);
	}
#endif

	if (lms_entity != world) {
		bprint( 2, "info_lmsdetect: too many info_lmsdetect entities in this map - you only need one.\n" );
		dremove(self);
	}

	lms_entity = self;

	m_precache = spawn();
	self.goalentity = m_precache;

#ifdef mtf_coop_expsys
	if (self.#lms_readfile == #TRUE && default_spawn == #FALSE)
	{
		file = open (fname, 0);
bprint(2,"NOT using def spawn!\n");
		// Get monster precache's
		a = "";
		do
		{
			a = read (file);
			if ( a == "precache_end" )
			{
				dprint ("info_lmsdetect: Monsters precache'd!\n");
				break;
			}
			if ( a == "//END" )
			{
				bprint (2, "info_lmsdetect: Missing precache_end in .tfi - fix your file!\n");
				self.#lms_readfile = #FALSE;
				break;
			}
			if ( a == "precache_set" )
			{
				a = read (file);
				while (a != "precache_end" && a != "//END")
				{
					SetSpawnFunc( m_precache, a );
					SetupMonsterPrecache( m_precache );
					a = read (file);
				}

				break;
			}
	
		} while (1);
		close(file);
	}

	if (self.#lms_readfile == #FALSE || default_spawn == #TRUE) {
#endif
bprint(2,"using def spawn!\n");
	// do all the precache's by calling the monsters' spawn functions
	m_precache.#spawnfunc = monster_army;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_dog;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_enforcer;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_demon1;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_fish;		// who knows, we might have a moat :p
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_knight;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_hell_knight;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_ogre;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_shalrath;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_tarbaby;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_wizard;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_zombie;
	SetupMonsterPrecache( m_precache );

	m_precache.#spawnfunc = monster_shambler;
	SetupMonsterPrecache( m_precache );
#ifdef mtf_coop_expsys
	}
#endif

	// TODO: add hipnotic/travail monsters too
	dremove(m_precache);	// k, we're done precache'ing - bai!

	// setup lives, but add/detract 'em based on player count
	if ( self.#lms_startteamlives == 0 )
		self.#lms_startteamlives = 10;
	team1lives = self.#lms_startteamlives;


	if (self.#lms_useroundlives == 0)
		self.#lms_useroundlives = 5;		// set a default value for lives per round (replenishes every round)
	if (self.#lms_usegloballives > 0)
		self.#lms_useroundlives = 0;		// set global lives for players only (look in tforttm.qc)

	total_monsters -= 13;

	self.#lms_starttime = time;
	self.#lms_level = 1;

	wait_for_players ();
};

void () lms_goalie_think =
{
	local float p_scale;
	local entity te;

	// find all resupply packs and adjust their wait time according to num_players
	#lms_startmatch_numplayers = 0;
	te = find ( world, classname, "player" );
	while (te)
	{
		if (te.playerclass != 0/* || te.#lms_replenish_lives == 1 */)
			#lms_startmatch_numplayers = #lms_startmatch_numplayers + 1;

		te = find (te, classname, "player");
	}

	if ( team1lives <= 0 || #lms_startmatch_numplayers == 0 ) {
		bprint(2, "BLUE TEAM HAS NO MORE LIVES - MONSTERS WIN\n");
		te = find ( world, classname, "func_monsterspawn" );
		while (te)
		{
			te.#spawn_active = 0;
			te = find (te, classname, "func_monsterspawn");
		}
		execute_changelevel ();
		self.nextthink = time + 999999999;
		return;
	}

	p_scale = ( 0.15 * (#lms_startmatch_numplayers - 1) ) + 1;				// allow more monsters if there are more players (cuz they spawn faster)
	//p_scale = 1;
	if ( (total_monsters - killed_monsters) > ceil(p_scale * 10) ) {
		bprint( 2, "MONSTERS ARE OVER-RUNNING BLUE OUTPOST (" );
		bprint( 2, ftos( ceil(p_scale * 10) ) );
		bprint( 2, ") - ONE LIFE IS LOST!\n" );
		team1lives = team1lives - 1;
		bprint( 2, "REMAINING TEAM LIVES: " );
		bprint( 2, ftos( team1lives ) );
		bprint( 2, "\n" );
	}

	self.think = lms_goalie_think;
	self.nextthink = time + 5;
};

void () spawnwave_resttime =
{
	local entity te;
	te = find ( world, classname, "func_monsterspawn" );
	while (te)
	{
		te.#spawn_active = 0;
		te = find (te, classname, "func_monsterspawn");
	}

	// if we're using lives-per-round, reset all connected players' lives
	if (self.#lms_useroundlives > 0) {
		self.#lms_game_begun = #FALSE;
		te = find ( world, classname, "player" );
		while (te)
		{
			if (te.playerclass != 0 || te.#lms_replenish_lives == 1 ) {
				te.#lms_lives = self.#lms_useroundlives;
//				te.#lms_replenish_lives = 0;
				te.lives = -1;	// we don't use these..
			}

			te = find (te, classname, "player");
		}
	}

	if ( team1lives <= 0 )
		return;

	// TODO: fix cheesy messages
	bprint( 2, "WAVE " );
	bprint( 2, ftos( self.#lms_level - 1 ) );
	bprint( 2, " COMPLETE, PREPARE FOR NEXT WAVE!\n" );

	self.think = spawnwave;
	self.nextthink = time + #LMS_REST_TIME;
};

void () spawnwave =
{
	local entity te;
	local float exptotal;

	if ( team1lives <= 0 )
		return;

	if (self.#lms_level == 0) {
		bprint( 2, "spawnwave: invalid lms level\n" );
		return;
	}
	if (self.#lms_level > #LMS_LAST_LEVEL) {
		bprint( 2, "PLAYERS WIN!\n" );

		exptotal = killed_monsters * 3;
		// EXP 4 ALLZ!
		te = find ( world, classname, "player" );
		while (te)
		{
			if (te.coop_exp != -1 && /*te != other && */te.has_disconnected != 1) {
				//te.coop_exp = te.coop_exp + exptotal;
				MTF_Coop_AddExperience(te, world, exptotal);
				MTF_Coop_PreWriteExperience( te, te.connect_name, te.coop_exp );

				stuffcmd(te, "cmd sayexp\n");
			}
			te = find (te, classname, "player");
		}
		execute_changelevel ();
		self.nextthink = time + 999999999;
		return;
	}

	self.#lms_game_begun = #TRUE;

	// TODO: do the fancy lettering crap..
	bprint( 2, "PREPARE FOR WAVE " );
	bprint( 2, ftos( self.#lms_level ) );
	bprint( 2, " OF LMS!\n" );

	te = find ( world, classname, "func_monsterspawn" );
	while (te)
	{
		te.#spawn_starttime = time;
		te.#lms_level = self.#lms_level;
		te.#spawn_active = 1;
		te = find (te, classname, "func_monsterspawn");
	}

	self.#lms_level++;
	self.think = spawnwave_resttime;
	self.nextthink = time + #LMS_MATCH_TIME;
};

// get a multiplier value for how fast/slow monsters will respawn
float ( float m_level ) GetLevelTiming =
{
#ifdef mtf_coop_expsys
	if ( lms_entity.#lms_override_speed > 0 ) {
		return lms_entity.#lms_override_speed;
	}
#endif

	if ( m_level == 8 ) // lots and lots of dogs :D
		return .5;
	else if ( m_level == 5 ) // lots of zombies and knights :D
		return 1;
	else if ( m_level == 10 ) // spaaaaaawwwwwnnnnsssss
		return .85;
	else if ( m_level == 2 || m_level == 3 || m_level == 4 )
		return 1.25;
	else if ( m_level == 1 )
		return 1;
	else {
		if ( m_level > 12 )
			return 1.1;
		else
			return 1.25;//m_level * .5;
	}
};

void () func_monsterspawn =
{
	local float rndthink, rndspawn, p_scale;
	local entity newmon;

	if ( !self.target )
		dprint( "func_monsterspawn: no target - can't tell monsters where to go!\n" );
	if ( self.#spawn_active == 0 || self.#lms_level == 0 ) {
		self.nextthink = time + 1;
		self.think = func_monsterspawn;
		return;
	}

//	self.#lms_level
//	self.#spawn_tries

	rndspawn = random();
	rndthink = rint(random() * 6 + 1);
//bprint(2,ftos(rndthink));
//bprint(2," is next think \n");

	if ( rndspawn < .5  || self.#spawn_tries == 3 ) {
		self.#spawn_tries = 0;

		newmon = spawn();

		total_monsters += 1;
//		WriteByte (2.000000, 27.000000);


			WriteByte (MSG_ALL, 38);
			WriteByte (MSG_ALL, 12);
			WriteLong (MSG_ALL, total_monsters);



		newmon.target = self.target;
		GetMonsterSpawn( newmon, self.#lms_level );

		newmon.#npc_spawndelay = 1;
		newmon.angles = self.#npc_origangle;
		newmon.#npc_origangle = self.#npc_origangle;
	//	newmon.netname = self.netname;
	//	newmon.classname = self.classname;
	//	newmon.old_owner = self;
		newmon.is_monster = 1;
		newmon.nextthink = time + 1;
		newmon.think = respawn_think;
		newmon.lives = -1;	// so we auto-remove the dead bodies (check respawn_think()) and so no exp is awarded till game's end
		if (mapname == "lms_keep")	// hack
			setorigin( newmon, self.origin + '0 0 65' );
		else
			setorigin( newmon, self.origin );

	}
	else
		self.#spawn_tries = self.#spawn_tries + 1;

	rndthink = rndthink * GetLevelTiming( self.#lms_level );

	p_scale = ( 0.15 * (#lms_startmatch_numplayers - 1) ) + 1;			// if more players, make monsters spawn faster
//bprint(2, ftos(p_scale));
//bprint(2, "\n");
	rndthink = rndthink / p_scale;

	self.think = func_monsterspawn;
	self.nextthink = time + rndthink;
};

void ( entity newmon, float m_level ) GetMonsterSpawn =
{
	local float rnd;
	local string m_cln;
	rnd = random();

#ifdef mtf_coop_expsys
	local string lname, fname, a, a1, a2, a3;
	local float file, c1, c2, c3;

	lname = strcat( "level_", ftos(lms_entity.#lms_level - 1) );

	if ( lms_entity.#lms_readfile == #TRUE ) {

		fname = frik_strcat( "maps/", mapname, ".tfi" );
		file = open (fname, 0);
		// Get Monsters From File
		a = "";
		do
		{
			a = read (file);
			if ( a == "//END" )
			{
				bprint (2, "info_lmsdetect: No monsters found - add some!\n");
				self.#lms_readfile = #FALSE;
				break;
			}
			if ( a == lname )
			{
				a = read (file);
				a = read (file);
				lms_entity.#lms_override_speed = stof(a);

				a = read (file);
				a1 = a;
				a1 = strzone(a1);
				a = read (file);
				c1 = stof(a);

				a = read (file);
				a2 = a;
				a2 = strzone(a2);
				a = read (file);
				c2 = stof(a);

				a = read (file);
				a3 = a;
				a3 = strzone(a3);
				a = read (file);
				c3 = stof(a);
				break;
			}
	
		} while (1);
		close (file);

		while ( 1 )
		{
			// TODO: make sure this doesn't break somehow?
			if (rnd > c1)
				m_cln = a1;
			else if (rnd > c2)
				m_cln = a2;
			else if (rnd > c3)
				m_cln = a3;
			rnd = random();
			if ( m_cln != "monster_none" )
				break;
		}
#ifdef DEBUG
dprint(m_cln );
dprint(" is spawning\n");
#endif
	}
	else
	{
#endif
	if ( m_level == 1 ) {
		if (rnd > .7)
			m_cln = "monster_army";
		else
			m_cln = "monster_dog";
	}
	else if ( m_level == 2 ) {
		if (rnd > .4)
			m_cln = "monster_army";
		else
			m_cln = "monster_dog";
	}
	else if ( m_level == 3 ) {
		if (rnd > .4)
			m_cln = "monster_army";
		else
			m_cln = "monster_enforcer";
	}
	else if ( m_level == 4 ) {
			m_cln = "monster_enforcer";
	}
	else if ( m_level == 5 ) {
		if (rnd > .6)
			m_cln = "monster_zombie";
		else
			m_cln = "monster_knight";
	}
	else if ( m_level == 6 ) {
		if (rnd > .4)
			m_cln = "monster_knight";
		else
			m_cln = "monster_hell_knight";
	}
	else if ( m_level == 7 ) {
		if (rnd > .8)
			m_cln = "monster_ogre";
		else if (rnd > .3)
			m_cln = "monster_hell_knight";
		else
			m_cln = "monster_knight";
	}
	else if ( m_level == 8 ) {
		m_cln = "monster_dog";		// really fast respawning dogs :p
	}
	else if ( m_level == 9 ) {
		if (rnd > .8)
			m_cln = "monster_tarbaby";
		else if (rnd > .5)
			m_cln = "monster_ogre";
		else
			m_cln = "monster_hell_knight";
	}
	else if ( m_level == 10 ) {
			m_cln = "monster_tarbaby";
	}
	else if ( m_level == 11 ) {
		if (rnd > .5)
			m_cln = "monster_shalrath";
		else
			m_cln = "monster_knight";
	}
	else if ( m_level == 12 ) {
		if (rnd > .7)
			m_cln = "monster_demon1";
		else if (rnd > .4)
			m_cln = "monster_shalrath";
		else
			m_cln = "monster_wizard";
	}
	else if ( m_level == 13 ) {
		if (rnd > .5)
			m_cln = "monster_demon1";
		else if (rnd > .4)
			m_cln = "monster_shambler";
		else
			m_cln = "monster_wizard";
	}
	else if ( m_level == 14 ) {
		if (rnd > .5)
			m_cln = "monster_shambler";
		else
			m_cln = "monster_zombie";
	}
#ifdef mtf_coop_expsys
	}
#endif

	SetSpawnFunc( newmon, m_cln );

};

// set spawn and netname info based on classname
// Add any new monsters here, etc.
void ( entity newmon, string newmon_cln ) SetSpawnFunc =
{
	newmon.classname = newmon_cln;

	if (newmon_cln == "monster_dog") {
		newmon.#spawnfunc = monster_dog;
		newmon.think1 = monster_dog1;
		newmon.netname = "rottweiler";
		newmon.classname = "monster_dog";
	} else if (newmon_cln == "monster_army") {
		newmon.#spawnfunc = monster_army;
		newmon.think1 = monster_army1;
		newmon.netname = "grunt";
		newmon.classname = "monster_army";
	} else if (newmon_cln == "monster_knight") {
		newmon.#spawnfunc = monster_knight;
		newmon.think1 = monster_knight1;
		newmon.netname = "knight";
		newmon.classname = "monster_knight";
	} else if (newmon_cln == "monster_hell_knight") {
		newmon.#spawnfunc = monster_hell_knight;
		newmon.think1 = monster_hell_knight1;
		newmon.netname = "hell knight";
		newmon.classname = "monster_hell_knight";
	} else if (newmon_cln == "monster_demon1") {
		newmon.#spawnfunc = monster_demon1;
		newmon.think1 = monster_demon2;
		newmon.netname = "fiend";
		newmon.classname = "monster_demon1";
	} else if (newmon_cln == "monster_shambler") {
		newmon.#spawnfunc = monster_shambler;
		newmon.think1 = monster_shambler1;
		newmon.netname = "shambler";
		newmon.classname = "monster_shambler";
	} else if (newmon_cln == "monster_wizard") {
		newmon.#spawnfunc = monster_wizard;
		newmon.think1 = monster_wizard1;
		newmon.netname = "scrag";
		newmon.classname = "monster_wizard";
	} else if (newmon_cln == "monster_enforcer") {
		newmon.#spawnfunc = monster_enforcer;
		newmon.think1 = monster_enforcer1;
		newmon.netname = "enforcer";
		newmon.classname = "monster_enforcer";
	} else if (newmon_cln == "monster_shalrath") {
		newmon.#spawnfunc = monster_shalrath;
		newmon.think1 = monster_shalrath1;
		newmon.netname = "vore";
		newmon.classname = "monster_shalrath";
	} else if (newmon_cln == "monster_tarbaby") {
		newmon.#spawnfunc = monster_tarbaby;
		newmon.think1 = monster_tarbaby1;
		newmon.netname = "spawn";
		newmon.classname = "monster_tarbaby";
	} else if (newmon_cln == "monster_ogre") {
		newmon.#spawnfunc = monster_ogre;
		newmon.think1 = monster_ogre1;
		newmon.netname = "ogre";
		newmon.classname = "monster_ogre";
	} else if (newmon_cln == "monster_fish") {
		newmon.#spawnfunc = monster_fish;
		newmon.think1 = monster_fish1;
		newmon.netname = "rotfish";
		newmon.classname = "monster_fish";
	} else if (newmon_cln == "monster_zombie") {
		newmon.#spawnfunc = monster_zombie;
		newmon.think1 = monster_zombie1;
		newmon.netname = "zombie";
		newmon.classname = "monster_zombie";
// HIPNOTIC
	} else if (newmon_cln == "monster_scourge") {
		newmon.#spawnfunc = monster_scourge;
		newmon.think1 = monster_scourge1;
		newmon.netname = "centroid";
	} else if (newmon_cln == "monster_gremlin") {
		newmon.#spawnfunc = monster_gremlin;
		newmon.think1 = monster_gremlin1;
		newmon.netname = "gremlin";
// ROGUE
	} else if (newmon_cln == "monster_morph") {
		newmon.#spawnfunc = monster_morph;
		newmon.think1 = monster_morph1;
		newmon.netname = "morpher";
	} else if (newmon_cln == "monster_wrath") {
		newmon.#spawnfunc = monster_wrath;
		newmon.think1 = monster_wrath1;
		newmon.netname = "wrath";
	} else if (newmon_cln == "monster_eel") {
		newmon.#spawnfunc = monster_eel;
		newmon.think1 = monster_eel1;
		newmon.netname = "eel";
// TRAVAIL
	} else if (newmon_cln == "monster_juggernaut") {
		newmon.#spawnfunc = monster_juggernaut;
		newmon.think1 = monster_juggernaut1;
		newmon.netname = "juggernaut";
	} else if (newmon_cln == "monster_spider") {
		newmon.#spawnfunc = monster_spider;
		newmon.think1 = monster_spider1;
		newmon.netname = "poisonous spider";
	}
	else
	{
		bprint(2, "SetSpawnFunc: Invalid or unsupported monster classname: ");
		bprint(2, newmon_cln );
		bprint(2, " - fix your .tfi!\n" );
	}
};