Assimilation Tutorial #2: Completely Random Spawnpoints


 

Posted by WarZone (209.223.137.*) on March 26, 1999 at 20:34:40:


qboolean findspawnpoint (edict_t *ent)
{
  vec3_t loc = {0,0,0};
  vec3_t floor;
  int i;
  int j = 0;
  int k = 0;

  trace_t tr;
  do {
    j++;
    for (i = 0; i < 3; i++)
      loc[i] = rand() % (8192 + 1) - 4096;

    if (gi.pointcontents(loc) == 0)
    {
      VectorCopy(loc, floor);
      floor[2] = -4096;
      tr = gi.trace (loc, vec3_origin, vec3_origin, floor, NULL, MASK_SOLID);
      k++;
      if (tr.surface & MASK_WATER)
        continue;
      VectorCopy (tr.endpos, loc);
      loc[2] += ent->maxs[2] - ent->mins[2]; // make sure the entity can fit!
    }
  } while (gi.pointcontents(loc) > 0 && j < 1000 && k < 500);

  if (j >= 1000 || k >= 500)
    return false;

  VectorCopy(loc,ent->s.origin);
  VectorCopy(loc,ent->s.old_origin);
  return true;
}

This function does all the work of placing an entity for you! Paste this function in g_utils.c, and add this line near the other g_utils.c function definitions in g_local.h (or at the bottom if you're lazy).
qboolean findspawnpoint (edict_t *ent);

Then when you spawn an entity, call this function on the entity before its origin gets set. Here's an example that will spawn players in random locations!


void PutClientInServer (edict_t *ent)
{
        vec3_t  mins = {-16, -16, -24};
        vec3_t  maxs = {16, 16, 32};
        int            index;
        vec3_t  spawn_origin, spawn_angles;
        gclient_t      *client;
        int            i;
        client_persistant_t    saved;
        client_respawn_t       resp;

        // find a spawn point
        // do it before setting health back up, so farthest
        // ranging doesn't count this client
/*--------------------------------*/
/*     new code starts here       */
/*--------------------------------*/
        VectorCopy (mins, ent->mins);
        VectorCopy (maxs, ent->maxs);
        if (findspawnpoint(ent)) // new line
        {
               VectorCopy(ent->s.origin, spawn_origin);
               VectorClear (spawn_angles);
               spawn_angles[yaw] = rand() % 360 - 180; // face a random direction
        }    
        else // couldn't find a good spot, so...
               SelectSpawnPoint (ent, spawn_origin, spawn_angles);
/*--------------------------------*/
/*     new code ends here         */
/*--------------------------------*/
        index = ent-g_edicts-1;
        client = ent->client;

I tested this code about 100 times on q2dm1 and it works perfectly!! The only hitch is that sometimes I spawned in mid-wall. That's because this code only check in an entity can "fit" in vertically, not horizontally. So, I'll leave that bit an an excercise for you to do.

You can use this code in a similar manner as the above example when spawning ANYTHING. You can now have weapons, armor, QUADs, techs, etc. spawning in completely random locations. Cool! When spawning in items, you should always check to see that findspawnpoint() returns true -- if not, that means that a decent spawn point couldn't be found. So, you'll probably need to assign the item its default spawn point in that case. Then again, if you want to risk an infinite loop (unlikely, but possible), you could do this:

while (findspawnpoint(ent) == false)
  {} // loop until a point is found..

If you've got any questions, feel free to ask.

Enjoy!
-- WarZone