Code3Arena

PlanetQuake | Code3Arena | Tutorials | << Prev | Tutorial 25 | Next >>

menu

  • Home/News
  • ModSource
  • Compiling
  • Help!!!
  • Submission
  • Contributors
  • Staff
  • Downloads

    Tutorials
    < Index >
    1. Mod making 101
    2. Up 'n running
    3. Hello, QWorld!
    4. Infinite Haste
    5. Armor Piercing Rails
    6. Bouncing Rockets
    7. Cloaking
    8. Ladders
    9. Favourite Server
    10. Flame Thrower
    11. Vortex Grenades
    12. Grapple
    13. Lightning Discharge
    14. Locational Damage
    15. Leg Shots
    16. Weapon Switching
    17. Scoreboard frag-rate
    18. Vortex Grenades II
    19. Vulnerable Missiles
    20. Creating Classes
    21. Scrolling Credits
    22. Weapon Dropping
    23. Anti-Gravity Boots
    24. HUD scoreboard
    25. Flashlight and laser
    26. Weapon Positioning
    27. Weapon Reloading
    28. Progressive Zooming
    29. Rotating Doors
    30. Beheading (headshot!)
    31. Alt Weapon Fire
    32. Popup Menus I
    33. Popup Menus II
    34. Cluster Grenades
    35. Homing Rockets
    36. Spreadfire Powerup
    37. Instagib gameplay
    38. Accelerating rockets
    39. Server only Instagib
    40. Advanced Grapple Hook
    41. Unlagging your mod


    Articles
    < Index >
    1. Entities
    2. Vectors
    3. Good Coding
    4. Compilers I
    5. Compilers II
    6. UI Menu Primer I
    7. UI Menu Primer II
    8. UI Menu Primer III
    9. QVM Communication, Cvars, commands
    10. Metrowerks CodeWarrior
    11. 1.27g code, bugs, batch


    Links

  • Quake3 Files
  • Quake3 Forums
  • Q3A Editing Message Board
  • Quake3 Editing


    Feedback

  • SumFuka
  • Calrathan
  • HypoThermia
  • WarZone





    Site Design by:
    ICEmosis Design


  •  
    TUTORIAL 25 - Flashlight and Lasersight
    by Drive C:

    Since I have just finished and corrected the code for these and I see that no one else has written a tutorial on this, I thought that I would.

    To introduce, this tutorial will produce the effects of lasersight and flashlight when implemented. The reason I have written this for BOTH flashlight AND lasersight is that the basic code for the entity and movement are the same and its simpler to write it once, right? You dont have to use both if you dont want to, its just here if you need it.

    We will be making changes all across the board here. Both cgame and game will be modified:

    g_cmds.c
    g_weapon.c

    cg_main.c ->really minor
    cg_ents.c

    g.local.h ->really minor
    cg_local.h ->really minor
    bg_public.h ->really minor

    Just one note before we begin. I am not big on making comments here in this tutorial, but please do. Everywhere you modify the code, make a comment. You do not know (maybe you do) how helpful this can be, so PLEASE do it->it might save you from doing a whole lot of things over again :).


    1. Setting Things Up -> the minor things

    We first have to declare some functions, variables, and types so lets do it!

    A1. Goto g_local.h, line ~490 and insert below:

    	void Weapon_HookFree (gentity_t *ent);
    	void Weapon_HookThink (gentity_t *ent);			
    	void Laser_Gen (gentity_t *ent, int type);
    	void Laser_Think( gentity_t *self );
    
    A2. Goto g_local.h, line ~274 and insert below:

    	int			lastKillTime;		// for multiple kill rewards
    
    	qboolean	fireHeld;			// used for hook
    	gentity_t	*hook;				// grapple hook if out			
    	gentity_t	*lasersight;			// lasersight OR flashlight if in use
    
    	// timeResidual is used to handle events that happen every second
    	// like health / armor countdowns and regeneration
    	int			timeResidual;
    
    B. Goto cg_local.h, line ~568 and insert below:

    	qhandle_t	viewBloodShader;
    	qhandle_t	tracerShader;
    	qhandle_t	crosshairShader[NUM_CROSSHAIRS];			
    	qhandle_t	laserShader;
    
    
    	qhandle_t	lagometerShader;
    
    
    C. Goto cg_ents.c, line ~7 and insert:

    	
    	static void CG_LaserSight( centity_t *cent );
    
    
    D. Goto cg_main.c, line ~585 and insert below:

    	cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" );
    	cgs.media.selectShader = trap_R_RegisterShader( "gfx/2d/select" );
    
    	for ( i = 0 ; i < NUM_CROSSHAIRS ; i++ ) {
    		cgs.media.crosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/crosshair%c", 'a'+i) );
    	}			
    	
    	cgs.media.laserShader = trap_R_RegisterShader( "sprites/laser" );
    	cgs.media.backTileShader = trap_R_RegisterShader( "gfx/2d/backtile" );
    	cgs.media.noammoShader = trap_R_RegisterShader( "icons/noammo" );
    	
    
    E. Goto bg_public.h, line ~585 and insert below:

    	ET_TELEPORT_TRIGGER,
    	ET_INVISIBLE,
    	ET_GRAPPLE,				// grapple hooked on wall			
    	ET_LASER,				// lasersight entity type
    
    	ET_EVENTS
    

    What we have done here is: (A1) declared the laser spawner/despawner function. (A2) Added a subordinate entity to gclient, so we can from gclient structure control the client's laser. (B) created a new shader for our lasersight in cgame. (C) Declared the laser sight draw function. (D) Told cgame what cg.media to assign to our shader. (E) created a new entity type ET_LASER.


    2. Creating the Command

    The first thing to do here is to give the client the ability to turn on or off the laser. If you are making a mod, you will most likely change this so that the client cannot simply type "laser" in the console, rather make it a powerup or something.

    As we all remember from before: go into g_cmds.c down to line ~1220 to the else if statements, and insert this code so that typing "laser" and "flashlight" in the console turn on/off the laser/flashlight.

    	else if (Q_stricmp (cmd, "gc") == 0)
    		Cmd_GameCommand_f( ent );
    	else if (Q_stricmp (cmd, "setviewpos") == 0)
    		Cmd_SetViewpos_f( ent );
    			
    	else if (Q_stricmp (cmd, "laser") == 0)
    		Laser_Gen( ent, 1 );//1=Laser, 2=Flashlight
    	else if (Q_stricmp (cmd, "flashlight") == 0)
    		Laser_Gen( ent, 2 );
    
    Basically, we have created two commands, which repond to console commands /laser and /flashlight, running our Laser_Gen function with 1 and 2, signifying flashlight and laser, repsectively.


    3. Create the Stupid Entity

    Go into g_weapon.c, at the very bottom: you can put it anywhere, but I place it here because it kinda pertains to weapon:
    				
    /*
    ============
    Laser Sight Stuff
    
    	Laser Sight / Flash Light Functions
    ============
    */
    
    void Laser_Gen( gentity_t *ent, int type )	{
    	gentity_t	*las;
    	int oldtype;
    
    	//Get rid of you?
    	if ( ent->client->lasersight) {
    		  oldtype = ent->client->lasersight->s.eventParm;
    		  G_FreeEntity( ent->client->lasersight );
    		  ent->client->lasersight = NULL;
    		  if (oldtype == type)
    			  return;
    	}
    
    	las = G_Spawn();
    
    	las->nextthink = level.time + 10;
    	las->think = Laser_Think;
    	las->r.ownerNum = ent->s.number;
    	las->parent = ent;
    	las->s.eType = ET_LASER;
    
    	//Lets tell it if flashlight or laser
    	if (type == 2)	{
    		las->s.eventParm = 2; //tells CG that it is a flashlight
    		las->classname = "flashlight";
    	}
    	else {
    		las->s.eventParm = 1; //tells CG that it is a laser sight
    		las->classname = "lasersight";
    	}
    
    	ent->client->lasersight = las;
    }
    
    void Laser_Think( gentity_t *self )	{
    	vec3_t		end, start, forward, up;
    	trace_t		tr;
    
    	//If Player Dies, You Die -> now thanks to Camouflage!
    	if (self->parent->client->ps.pm_type == PM_DEAD)  {
    		G_FreeEntity(self);
    		return;
    	}
    
    	//Set Aiming Directions
    	AngleVectors(self->parent->client->ps.viewangles, forward, right, up);
    	CalcMuzzlePoint(self->parent, forward, right, up, start);
    	VectorMA (start, 8192, forward, end);
    
    	//Trace Position
    	trap_Trace (&tr, start, NULL, NULL, end, self->parent->s.number, MASK_SHOT );
    
    	//Did you not hit anything?
    	if (tr.surfaceFlags & SURF_NOIMPACT || tr.surfaceFlags & SURF_SKY)	{
    		self->nextthink = level.time + 10;
    		trap_UnlinkEntity(self);
    		return;
    	}
    
    	//Move you forward to keep you visible
    	if (tr.fraction != 1)	VectorMA(tr.endpos,-4,forward,tr.endpos);
    
    	//Set Your position
    	VectorCopy( tr.endpos, self->r.currentOrigin );
    	VectorCopy( tr.endpos, self->s.pos.trBase );
    
    	vectoangles(tr.plane.normal, self->s.angles);
    
    	trap_LinkEntity(self);
    
    	//Prep next move
    	self->nextthink = level.time + 10;
    }
    
    
    
    Well that will do all the stuff that creates, deletes, and moves our entity. The first function generates the entity and sets it into thinking every 1/100 second, and it thinks in the bottom function by tracing the player's view, and setting the laser's position at the end of the trace. Just some notes: I like to sneak variables into unused slots whenever possible to prevent over-modification (like the weapon drop function -> there're no need for all those changes he made). This is the reason why I have chosen s.eventparm to sneak whether its a lasersight or flashlight. Then, in cgame, we will be able to differentiate and draw the thing.


    4. Color it, Light it, Whatever

    These are modifications to cgame so that an ET_LASER will either create a sprite, or a light, whichever it actually is. To start, we have to modify the switch statement in cg_ents.c at about ln. 712.
    	case ET_SPEAKER:
    		CG_Speaker( cent );
    		break;
    	case ET_GRAPPLE:
    		CG_Grapple( cent );
    		break;			
    	case ET_LASER:
    		CG_LaserSight( cent );
    		break;
    	}
    }
    
    This just tells us that when we have an ET_LASER centity, we will call CG_LaserSight, which will be responsible for actually adding the graphic to the scene. At the bottom of cg_ents.c:
    /*
    ==================
    CG_LaserSight
      Creates the laser
    ==================
    */
    
    static void CG_LaserSight( centity_t *cent )  {
    	refEntity_t			ent;
    
    
    	// create the render entity
    	memset (&ent, 0, sizeof(ent));
    	VectorCopy( cent->lerpOrigin, ent.origin);
    	VectorCopy( cent->lerpOrigin, ent.oldorigin);
    
    	if (cent->currentState.eventParm == 1)
    	{
    		ent.reType = RT_SPRITE;
    		ent.radius = 2;
    		ent.rotation = 0;
    		ent.customShader = cgs.media.laserShader;
    		trap_R_AddRefEntityToScene( &ent );
    	}
    	else	{
    		trap_R_AddLightToScene(ent.origin, 200, 1, 1, 1);
    	}
    
    	
    }
    
    This function is fairly simple. What it does first is that it differentiates the laser and flashlight. If it is a lasersight, then it sets the refentity to a sprite with our shader. If not, it simply adds a white light with intensity 200 (which turns out to be directly related to the radius of the spot). Basically, this would be the end of the story except for one thing -> where we gonna get our shader?


    5. Screw it, We're Done

    Yeah I wish. Fortunately, you can go ahead and compile, cause we're done with the source. Now we have to make 1. your shader and 2. your graphic. This should be a piece of cake for all you shader people out there. If not, then create a file in the /scripts directory with extension .shader (or append a preexisting shader). Here it is:
    
    sprites/laser
    {
    	cull disable
    	{
    		clampmap sprites/laser.jpg
    		blendfunc GL_ONE GL_ONE
            }
    }
    

    Now we have to make our image. Fortunately, for all y'all who cant make any images, I have one ready for you: For those who cannot read the above shader, you won't know where to put it, and I'm not gonna say, you should be able to figure it out :). Well guys, that's it. We're done, so go play your game with your laser sight, and make whatever modifications you need to make, cause c-mon, you cant just copy and paste, you have to learn. So anyway, thanks for letting me write this and I think maybe this will be worth something to someone. Oh, BTW, thanks to Quake DeveLS for letting me look over the Q2 laser. It really made me see why mine wasnt working (dang linkentity).

    Good Luck all coders -> Drive C:

    PlanetQuake | Code3Arena | Tutorials | << Prev | Tutorial 25 | Next >>