earthQUAKE is a Quake mod, based around a core of original Quake game assets, but with additional maps, models, textures, sound and code. Not all of those additional items are my own, sole, creations. The maps are all mine, gameplay/plot ideas are all mine, but some of the art and indeed code is sourced from the many good people who share their talents for free. They will be credited fully when the mod is finished and released. The mod will take place within one map; a large detailed environment wthin which both the narrative and gameplay will change and develop as the mod progresses. In addition to the mod, I'm writing a background story, imaginatively entitled "earthQUAKE: The Beginning", which will define the world and experiences of the mod that follows. There will also be an "after" story, to be written and uploaded after the mod is completed.

Post news Report RSS The Gate

An explanation of the gate, what it does, what it's for and how it works

Posted by on

"The Gate" is an immensely important feature within earthQUAKE - it provides a crucial gameplay mechanic in terms of allowing a single map to change and evolve in respect of the enemies you face, while also providing a narrative feature to both scare and, hopefully, engage the player.
The gameplay mechanic is key - there's one of me, and making a comparatively huge map like eq0101.bsp is an ongoing and massive task for someone who is essentially a hobbyist, doing it in their spare time. It's tidy way of being able to introduce enemies into the map making the playtime longer and the gameplay organic.

In terms of the narrative, The Gate arrived on the night the world was decimated (again read the background story: here ) it's inserted into the world, destroying a building by carving itself into it. From then on it was able to teleport Quake monsters into the human world and allow them to return to their world though it.

After the initial invasion, it lies dormant, although monsters can and do arrive through it. In gameplay terms this may be triggered by a previous group of monsters all being killed, by an 'event' (narratively speaking) such as finding something, entering a new are/building etc. Its that flexibility that will allow gameplay to be tell a story.

Anyway, to code. There's 4 things the code must do

  • Recognise that an event has happened
  • Trigger the gate 'firing' (lightening hitting the towers from the sky)
  • Play a sound to indicate the arrival of the monsters
  • Teleport the monsters in.

In addition all of the above needs to be configurable, so, depending on which event has triggered the gate (effectively, which part of the story the player is at) the timings between the event happening, the gate firing (and how long it fires for), the sound playing and the monsters teleporting in, can all be customisable. Obviously the code alos needs to allow different groups or individual monsters to enter depending, again, on the story.

I'll start with timings

// Gate Timings ********************************************************************
float gate_fire_delay = 10;// time before lightning fires gate after goal achieved *
float teleport_delay = 10;// time before teleport happens after lightning finishes *
float gate_fire_length = 10;// duration of lightning bursts                        *
// End of Gate Timings *************************************************************

This is work-in progress code. Each variable will be , in the future, dependent on the 'goal' the player is current engaged in, and will be set to a different time span (the 10 is 10 seconds) as appropriate.
Next the gate firing:

//=====================================================================================================================
//=       Gate lightning firing   START                                                                               = 
//=====================================================================================================================
 
// Credits *****************************
// Hipnotic and especially Dr Shadowborg
// *************************************

void () lightningbars_use = 
{ 
	if (goal_finished == 0)
		self.state = 1;
	else
		self.state = 0;
}; 

void () lightningbars_think = { 

   	self.think = lightningbars_think; 
   	self.nextthink = time + self.wait;

	if (goal_finished == 0)
 	{ 
     		self.attack_finished = time + gate_fire_length; //length of time lightning fires for
     		self.state = TRUE; 
		if (lightning_count >= 6) // check to make sure all towers have fired before ending firing/resetting variables
		{
			goal_readjust(); // reset of variables, will do so dependent on next goal
			lightning_count = 0; // allows the gate to be fired again
		}
	} 

	if ((self.attack_finished<= time) &amp;&amp; (lightning_count >= 6)) // checking that all towers have fired &amp; for long enough
	{
		teleport_next_enemies(); // go to the teleporting enemies routine
	}
   
        if(self.attack_finished <= time) 
	{   	
		self.state = 0; 
	}

        if(self.state == 0) 
  		return; 

	self.ltime = time + 0.25; 

	sound (self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);

	if(!self.enemy) 
	{ 
        	self.enemy = find (world, targetname, self.target); 
        	if (!self.enemy) 
        	objerror ("couldn't find target"); 
	} 

        WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); 
        WriteByte (MSG_BROADCAST, TE_LIGHTNING1); 
        WriteEntity (MSG_BROADCAST, self); 
        WriteCoord (MSG_BROADCAST, self.origin_x); 
        WriteCoord (MSG_BROADCAST, self.origin_y); 
        WriteCoord (MSG_BROADCAST, self.origin_z); 
        WriteCoord (MSG_BROADCAST, self.enemy.origin_x); 
        WriteCoord (MSG_BROADCAST, self.enemy.origin_y); 
        WriteCoord (MSG_BROADCAST, self.enemy.origin_z); 

	lightning_count = lightning_count + 1;
}; 

void() lightningbars_pre_think =
{
	if (goal_finished == 0)
	{
		self.think = lightningbars_think;
		self.nextthink = time + gate_fire_delay;
	}
	if (goal_finished >= 1)
	{
		self.think = lightningbars_pre_think;
		self.nextthink = time + 0.01;
	}
}; 

void() event_lightningbars = 
{ 
   if(!self.wait) 
    self.wait = 0;// eQ setting: 0 to make constant stream 

   self.classname = "lightningbars"; 
   self.solid = SOLID_NOT; 
   self.movetype = MOVETYPE_NONE; 
   self.model = string_null; 
 
   self.ltime = time; 

  self.think = lightningbars_pre_think; 

 self.nextthink = time + 3; // so that it has time to load the map 
 self.use = lightningbars_use; 
}; 

//=====================================================================================================================
//=       Gate lightning firing  END                                                                                  = 
//=====================================================================================================================

This code does the following:

  • Creates 'lightningbars' the actual lightning effetc between the sky and each of the 6 towers
  • They are made within a "think" which allows me to set delays and check if the current goal is finished or not
  • it calls a goal readjust function to move the code onto the next goal
  • it counts that all lightning bars have fired and then resets the count for the next firing
  • it then calls the function to teleport in the next enemies

The next code teleports in the enemies:

//====================================================================================================================
//=        Teleporting Enemies to Gate   START                                                                       =
//====================================================================================================================

void() eport_post_noise =
{
	sound (self, CHAN_VOICE, "misc/null.wav", 1); // removes eport_pre_noise as enemies 'port in
};

void () fire_enemyports = //send next group of enemies to gate
{
	//local entity player;      
       	//player = find(world, classname, "player");
	//centerprint (player, "fire eport......\n"); //just for testing...

	eport_post_noise();
	
	if (next_goal_start == 2)
	{
		self.target = "eport1"; // first enemyport to be fired
		self.delay = 0; //staggering of enemies porting in
		SUB_UseTargets();
		self.target = "eport2";
		self.delay = 4;
		SUB_UseTargets();
		self.target = "eport3";
		self.delay = 8;
		SUB_UseTargets();
		self.target = "eport4";
		self.delay = 12;
		SUB_UseTargets();
	}
	else if (next_goal_start == 3)
	{
		self.target = "eport5";
		self.delay = 0;
		SUB_UseTargets();
		self.target = "eport6";
		self.delay = 4;
		SUB_UseTargets();
		self.target = "eport7";
		self.delay = 8;
		SUB_UseTargets();
		self.target = "eport8";
		self.delay = 12;
		SUB_UseTargets();
	}
};

void () teleport_next_enemies_think =
{
		//local entity player;      
       		//player = find(world, classname, "player");
		//centerprint (player, "They're coming......\n"); //just for testing...
		mission_update();
		fire_enemyports();		
};	

void () eport_pre_noise =
{
		sound (self, CHAN_VOICE, "misc/eport_arrival.wav", 1, ATTN_NORM);
};

void () teleport_next_enemies =
{
		eport_pre_noise();
		self.think = teleport_next_enemies_think;
		self.nextthink = time + teleport_delay;
};

//=====================================================================================================================
//=        Teleporting Enemies to Gate    END                                                                         =
//=====================================================================================================================

The first stage of this, although at the bottom of the above, calls the "terrifying_monsters_come_soon" noise, and again uses "think" functiosn to allow for customisable delays
After making the noise, it calls the mission_update function to move the player on to the next goal, the sets about teleporting in the next set of monsters. This code is heavily based on the original quake teleporting code. It is also somewhat work-in-progress as I may move from teleporting monsters from "boxes" hidden in the map, to an even more code based solution, by spawning monsters from scratch. As it stands it ports in a different set of monsters dependent upon the current goal.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: