Team Location, Taunt, and Miscellaneous

Not all of the maps have "target_location" on them, which report where a player is. Here's how you can spawn them in on maps when the weapons spawn. In g_spawn.c G_CallSpawn

        // check item spawn functions
        for ( item=bg_itemlist+1 ; item->classname ; item++ ) {
               if ( !strcmp(item->classname, ent->classname) ) {
                       // found it
                       if( item->giType == IT_TEAM && g_gametype.integer != GT_CTF ) {
                               return qfalse;
                       }
                       G_SpawnItem( ent, item );
//freeze
                       Location_Spawn( ent );
//freeze
                       return qtrue;
               }
        }

In g_freeze Location_Spawn

void SP_target_location( gentity_t *self );
void Location_Spawn( gentity_t *it_ent ) {
        gentity_t      *ent;
        int     i;
 
        if ( !it_ent->classname ) {
               return;
        }
        if ( !( it_ent->item && it_ent->item->pickup_name ) ) {
               return;
        }
 
        for ( i = 0; nearbyitems[ i ]; i++ ) {
               if ( Q_stricmp( it_ent->classname, nearbyitems[ i ] ) ) {
                       continue;
               }
 
               ent = G_Spawn();
               ent->classname = "target_location";
               ent->message = it_ent->item->pickup_name;
// This is important for later
               ent->count = -1;
               VectorCopy( it_ent->r.currentOrigin, ent->s.origin );
               SP_target_location( ent );
 
               break;
        }
}

This runs through a list of items to see if we want to generate a "target_location" at that spot. We make the team location message be that item's pickup name. Here's our list

char    *nearbyitems[] = {
        "item_armor_combat",
        "item_armor_body",
        "item_health_mega",
        "weapon_shotgun",
        "weapon_grenadelauncher",
        "weapon_rocketlauncher",
        "weapon_lightning",
        "weapon_railgun",
        "weapon_plasmagun",
        "weapon_bfg",
        "holdable_medkit",
        "item_quad",
        "item_enviro",
        "item_haste",
        "item_invis",
        "item_regen",
        "item_flight",
        "team_CTF_redflag",
        "team_CTF_blueflag",
        NULL
};

Now, I want to make sure these aren't made on a map that already has location spots. In g_target.c target_location_linkup

        int n;
//freeze
        qboolean       locationLinked = qfalse;
//freeze
 
        if (level.locationLinked) 
               return;
//freeze
        for ( i = 0, ent = g_entities; i < level.num_entities; i++, ent++ ) {
               if ( ent->classname && !Q_stricmp( ent->classname, "target_location" ) ) {
// Do we have one that wasn't "made" by my Location_Spawn?
                       if ( ent->count != -1 ) {
                               locationLinked = qtrue;
                               break;
                       }
               }
        }
//freeze
 
        level.locationLinked = qtrue;
 
        level.locationHead = NULL;
 
        trap_SetConfigstring( CS_LOCATIONS, "unknown" );
 
        for (i = 0, ent = g_entities, n = 1;
                       i < level.num_entities;
                       i++, ent++) {
               if (ent->classname && !Q_stricmp(ent->classname, "target_location")) {
//freeze
                       if ( ent->count == -1 ) {
// This will remove the item if we don't need it
                               if ( locationLinked ) {
                                      ent->think = G_FreeEntity;
                                      ent->nextthink = level.time + FRAMETIME;
 
                                      continue;
                               }
 
                               ent->count = 0;
                       }
//freeze
                       // lets overload some variables!
                       ent->health = n; // use for location marking
                       trap_SetConfigstring( CS_LOCATIONS + n, ent->message );

Note that function is only run once. In g_team.c Team_GetLocation we make sure if you're frozen and flying around, it reports where you died, not where you are. This fixes the team overlay and when you speak to your team.

        for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) {
               len = ( origin[0] - eloc->r.currentOrigin[0] ) * ( origin[0] - eloc->r.currentOrigin[0] )
                       + ( origin[1] - eloc->r.currentOrigin[1] ) * ( origin[1] - eloc->r.currentOrigin[1] )
                       + ( origin[2] - eloc->r.currentOrigin[2] ) * ( origin[2] - eloc->r.currentOrigin[2] );
 
//freeze
               if ( IsFrozen( ent ) ) {
                       if ( eloc->health == ent->client->pers.teamState.location ) {
                               return eloc;
                       }
               }
//freeze
               if ( len > bestlen ) {
                       continue;
               }

To have the player be able to taunt we make it so if they taunt while near a frozen body, that frozen body will make a taunt sound. First, in g_active.c ClientThink_real

        // Let go of the hook if we aren't firing
        if ( client->ps.weapon == WP_GRAPPLING_HOOK &&
               client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) {
               Weapon_HookFree(client->hook);
        }
//freeze
        if ( ucmd->buttons & BUTTON_GESTURE ) {
               FreezeAnimate( ent );
        }
//freeze
 
        // set up for pmove
        oldEventSequence = client->ps.eventSequence;

And then in g_freeze FreezeAnimate

void FreezeAnimate( gentity_t *ent ) {
        int     i;
        gentity_t      *e;
        vec3_t  delta;
 
// Don't allow them to taunt all the time
// Using this fly_sound_debounce_time for my own whim
        if ( ent->fly_sound_debounce_time > level.time ) {
               return;
        }
        if ( !IsFrozen( ent ) ) {
               return;
        }
 
// Search all entities
        for ( i = 0, e = g_entities; i < level.num_entities; i++, e++ ) {
               if ( !IsBody( e ) ) {
                       continue;
               }
// Is the body on our team?
               if ( e->splashDamage != ent->client->sess.sessionTeam ) {
                       continue;
               }
 
               VectorSubtract( e->s.pos.trBase, ent->s.pos.trBase, delta );
// Are we close enough?
               if ( VectorLength( delta ) > 192 ) {
                       continue;
               }
 
// Make that body make a noise
               G_AddEvent( e, EV_TAUNT, e->r.ownerNum );
               ent->fly_sound_debounce_time = level.time + ( 34 * 66 + 50 );
               break;
        }
}

We are using ownerNum as an eventParm. In cg_event.c CG_EntityEvent

        case EV_TAUNT:
               DEBUGNAME("EV_TAUNT");
/*freeze
               trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
freeze*/
               trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->eventParm ? es->eventParm : es->number, "*taunt.wav" ) );
//freeze
               break;

If we have an eventParm then use it. I do this because the body entity doesn't have a "number", so we pass the number of the entity that created it (which we happened to remember in the body's ownerNum). This will allow CG_CustomSound to create the right "*taunt.wav" for that body.

Now some miscellaneous stuff. Force the server to always be in team deathmatch or CTF. In g_main.c G_RegisterCvars

        // check some things
 
/*freeze
        if ( g_gametype.integer < 0 || g_gametype.integer >= GT_MAX_GAME_TYPE ) {
               G_Printf( "g_gametype %i is out of range, defaulting to 0\n", g_gametype.integer );
               trap_Cvar_Set( "g_gametype", "0" );
        }
freeze*/
        if ( g_gametype.integer < GT_TEAM || g_gametype.integer >= GT_MAX_GAME_TYPE ) {
               G_Printf( "g_gametype %i is out of range, defaulting to 3\n", g_gametype.integer );
               trap_Cvar_Set( "g_gametype", "3" );
        }
//freeze
 
        level.warmupModificationCount = g_warmup.modificationCount;

In CheckIntermissionExit, let the maps rotate even if noone's playing.

        // if nobody wants to go, clear timer
/*freeze
        if ( !ready ) {
freeze*/
        if ( !ready && notReady ) {
//freeze
               level.readyToExit = qfalse;
               return;
        }

In g_items.c Pickup_Weapon, don't wait 30 seconds to respawn a weapon.

        if (ent->item->giTag == WP_GRAPPLING_HOOK)
               other->client->ps.ammo[ent->item->giTag] = -1; // unlimited ammo
 
/*freeze
        // team deathmatch has slow weapon respawns
        if ( g_gametype.integer == GT_TEAM ) {
               return RESPAWN_TEAM_WEAPON;
        }
freeze*/
 
        return g_weaponRespawn.integer;

In cg_info.c CG_DrawInformation, display "Freeze Tag" for the client when the level loads.

        case GT_TOURNAMENT:
               s = "Tournament";
               break;
        case GT_TEAM:
/*freeze
               s = "Team Deathmatch";
freeze*/
               s = "Freeze Tag";
//freeze
               break;
        case GT_CTF:
/*freeze
               s = "Capture The Flag";
freeze*/
               s = "Freeze Tag CTF";
//freeze
               break;
        default:
               s = "Unknown Gametype";
               break;

Hope this information has helped you in designing your mod.

Return