Basic client muzzle & 3d hud code


 

Posted by outlaw (80.130.67.*) at 10:01 AM, 3/22/2002:

Basic Clientmuzzle & 3D Hud Tutorial

This tutorial is divided into two parts:
- the first part describes how to create a simple 3D Hud which is accessable from the game dll.
- the second pard describes how to spawn a Muzzleflash entity on this Hud.



1. 3D Hud

The first thing a 3D hud needs are objects. These objects have to
be available in the game dll for easier use. They also have to be available
on the client. A good object with this properties is an entity.
You also can give entities different effects like EF_QUAD.
Sometimes they will lag down clients and servers, but since
Aimbots are easy to create Quake2 is only playable on LAN.

So let us start with the shit... ;) the first thing we need is
an identification for a Hud model - a flag. A flag which stays
for the main Hud model is the RF_WEAPONMODEL flag. We will use
it to tell the client which is a Hud model and which not.
The next thing we need is a pointer to the owner: ent->owner.
A Hud model will only be sent to this owner - not to the other clients.
So we need the source to block it... and her it is:

open up "sv_ents.c" from your Quake2.exe source and add this code
in SV_BuildClientFrame:
<C&NBSP;CODE>
[...]
    // ignore ents without visible models unless they have an effect
    if (!ent->s.modelindex && !ent->s.effects && !ent->s.sound
      && !ent->s.event)
      continue;

    // ignore if not touching a PV leaf

    //==== added (RF_WEAPONMODEL) ====
    if (ent->s.renderfx & RF_WEAPONMODEL)
    {
      if (clent != ent->owner)
        continue;
    } else
    //========

    if (ent != clent)
    {
      // check area
      if (!CM_AreasConnected (clientarea, ent->areanum))
      {  // doors can legally straddle two areas, so
        // we may need to check another one
        if (!ent->areanum2
          || !CM_AreasConnected (clientarea, ent->areanum2))
          continue;    // blocked by a door
      }
[...]
</C&NBSP;CODE>

Ok - this is the stuff on the server. Now we need a function on the client.
This function looks thru all entities and does some special calculations for
the Hud models: first it will rotate the entities to the right position
in the clients view. The secont thing it does is to copy the clients angles
to the entity. At last it will move it to right screen position:

you have to insert the following code into "cl_ents.c". Scroll
down to CL_AddPacketEntities.
<C&NBSP;CODE>
[...]
    else
    {  // interpolate angles
      float  a1, a2;

      for (i=0 ; i<3 ; i++)
      {
        a1 = cent->current.angles[i];
        a2 = cent->prev.angles[i];
        ent.angles[i] = LerpAngle (a2, a1, cl.lerpfrac);
      }
    }


    
    //==== added (RF_WEAPONMODEL) ====

    if (renderfx & RF_WEAPONMODEL)
    {
      int n;      

      // rotate the model position
      for (n=0; n<3; n++)
      {
        int    n_prev = (n==2) ? (0) : (n+1);
        int    n_real = (n_prev==2) ? (0) : (n_prev+1);
        float  sin_w = sin (cl.refdef.viewangles[n_real] * 3.1415926536897932384626433832795 / 180);
        float  cos_w = cos (cl.refdef.viewangles[n_real] * 3.1415926536897932384626433832795 / 180);
        float  temp_new = ent.origin[n_prev];
        float  temp_old = ent.oldorigin[n_prev];

        ent.origin[n_prev] = cos_w * temp_new - sin_w * ent.origin[n_real];
        ent.origin[n_real] = sin_w * temp_new + cos_w * ent.origin[n_real];
        ent.oldorigin[n_prev] = cos_w * temp_old - sin_w * ent.oldorigin[n_real];
        ent.oldorigin[n_real] = sin_w * temp_old + cos_w * ent.oldorigin[n_real];
      }

      // copy player angles
      ent.angles[0] = cl.refdef.viewangles[0];
      ent.angles[1] = cl.refdef.viewangles[1];
      ent.angles[2] = -cl.refdef.viewangles[2];

      // move the model to vieworg
      VectorAdd(ent.origin, cl.refdef.vieworg, ent.origin);
      VectorAdd(ent.oldorigin, cl.refdef.vieworg, ent.oldorigin);
    }
    //========

    if (s1->number == cl.playernum+1)
    {
      ent.flags |= RF_VIEWERMODEL;  // only draw from mirrors
      // FIXME: still pass to refresh

      if (effects & EF_FLAG1)
        V_AddLight (ent.origin, 225, 1.0, 0.1, 0.1);
      else if (effects & EF_FLAG2)
        V_AddLight (ent.origin, 225, 0.1, 0.1, 1.0);
      else if (effects & EF_TAGTRAIL)            //PGM
        V_AddLight (ent.origin, 225, 1.0, 1.0, 0.0);  //PGM
      else if (effects & EF_TRACKERTRAIL)          //PGM
        V_AddLight (ent.origin, 225, -1.0, -1.0, -1.0);  //PGM

      continue;
    }
[...]
</C&NBSP;CODE>

Thats it - the whole 3D Hud code !!! (whew ;)



2. Basic Client Muzzle

Now we only have to spawn an entity with the RF_WEAPONMODEL flag
turned on and an owner client:

open "g_local.h" and add a pointer to our Muzzle entity:
put this code at the very end of the file:
<C&NBSP;CODE>
  gitem_t    *item;      // for bonus items

  // common data blocks
  moveinfo_t    moveinfo;
  monsterinfo_t  monsterinfo;

  //==== added (player muzzle) ====
  edict_t    *muzzle_ent;
  //========
};
</C&NBSP;CODE>

now you have to spawn a Muzzle entity, if a player uses his weapon...
add this code in Weapon_Generic:
<C&NBSP;CODE>
[...]
  if (ent->client->weaponstate == WEAPON_FIRING)
  {
    for (n = 0; fire_frames[n]; n++)
    {
      if (ent->client->ps.gunframe == fire_frames[n])
      {
        if (ent->client->quad_framenum > level.framenum)
          gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);

        //==== added (player muzzle) ====
        if (!ent->muzzle_ent)
        {
          if (ent->client->ps.gunindex)
          {
            ent->muzzle_ent = G_Spawn();
            VectorSet(ent->muzzle_ent->s.origin, 40, -12, 0);
            VectorCopy(ent->muzzle_ent->s.origin, ent->muzzle_ent->s.old_origin);
            ent->muzzle_ent->s.modelindex = gi.modelindex("sprites/s_flash.sp2");
            ent->muzzle_ent->s.renderfx = RF_WEAPONMODEL | RF_FULLBRIGHT;
            ent->muzzle_ent->s.effects = EF_SPHERETRANS;
            ent->muzzle_ent->owner = ent;
            gi.linkentity(ent->muzzle_ent);
          }
        }
        //========

        fire (ent);
        break;
      }
    }
[...]
</C&NBSP;CODE>

At last you have to delete the Muzzleflash. The code I used to
do this is not very nice because it deletes the Muzzle every frame
but it works... and this night is LAN party in town - no time to optimize
the code ;)

add this at the top of Weapon_Generic:
<C&NBSP;CODE>
void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, int *fire_frames, void (*fire)(edict_t *ent))
{
  int    n;

  //==== added (player muzzle) ====
  if (ent->muzzle_ent)
  {
    G_FreeEdict(ent->muzzle_ent);
    ent->muzzle_ent = NULL;
  }
  //========

  if(ent->deadflag || ent->s.modelindex != 255) // VWep animations screw up corpses
  {
    return;
  }
[...]
</C&NBSP;CODE>

Thats it, sorry for the poor discriptions but I have no time and
it seems to me that you guys are waiting for this Muzzle stuff... ;)

- outlaw