HighScore “Ghost” Follower

 

 

philip
profile | email

posted 11-24-98 4:02 PM CT (US)
Title: HighScore 'Ghost' Follower
Difficulty: Moderate
By: Philip (aka Maj.Bitch)
Email: peblair@gv.net
Date: 11-24-98
Note: Please give credit where credit is due.
======================================================

This is a takeoff of my Ghost 'bot detection' stuff I put together a few months ago. I put this together because I thought that a player should be able to visibly 'see' who is the player on top of the frag-board! Currently there is no way to tell just by looking at the scoreboard because in the game you really can't tell exactly who that top-player is!! Well, now you can!!

How it works:

There is a server-side variable which you set in your config file which toggles a Ghost entity to follow around the player with the highest score! When the Ghost is turned ON a particle effect will appear 50 units above the head of the player with the highest score thereby letting everybody know who is on top of the scoreboard!!

The Ghost entity follows this player around as they move (except when they are dead, respawning, or of course, have left the game). The Ghost is in a loop checking every 1 second to see if another player has taken the top spot on the board. If so, then they become the player which the Ghost will be following around! The other players will see a sprinkle of particles above the head of the player who is currently with the top score. This will give the other players an opportunity to go and chase the bastard down (or avoid him! Cowards!!).

NOTE: Code could easily be inserted to give the player an extra frag or two if they kill the player with the Ghost entity above their heads! You decide what is appropriate for your particular mod.

Okay, let's get started..

============================================================
Let's add a server config variable to your g_local.h file.

extern cvar_t *g_select_empty;
extern cvar_t *dedicated;
extern cvar_t *sv_gravity;
extern cvar_t *sv_maxvelocity;
extern cvar_t *sv_bestplayer; //<=== NEW VAR HERE

============================================================
At the top of you g_main.c file we need to add this:

cvar_t *sv_maxvelocity;
cvar_t *sv_gravity;
cvar_t *sv_bestplayer; // <== DECLARE VAR HERE

This will formally declare the variable.

============================================================
In your InitGame() function add this line:

void InitGame(void)
{

// change anytime vars
dmflags = gi.cvar("dmflags", "0", CVAR_SERVERINFO);
fraglimit = gi.cvar("fraglimit", "0", CVAR_SERVERINFO);
timelimit = gi.cvar("timelimit", "0", CVAR_SERVERINFO);
password = gi.cvar("password", "", CVAR_USERINFO);

--------- ADD THIS ONE LINE RIGHT HERE -----------------
sv_bestplayer=gi.cvar("sv_bestplayer","1",CVAR_SERVERINFO);


This will set the variable's state.

============================================================
Lastly, open up your p_client.c file..

Okay, now add these lines to your ClientBeginDeathmatch()
function as shown:

void ClientBeginDeathmatch(edict_t *ent) {
static qboolean Ghost_Spawned=false; //<=== NEW LINE HERE

------------- ADD THESE LINES RIGHT HERE --------------

// Create Ghost on first Player connect.
if ((sv_bestplayer->value==1.0) && !Ghost_Spawned) {
Create_Ghost();
Ghost_Spawned=true; }


This will spawn the Ghost entity when the very first player
connects to your server..

============================================================
At the top of your p_client.c file, add these new routines:

NOTE: YOU MAY ALREADY HAVE THESE HELPER ROUTINES!!!

//======================================================
// True if Ent is valid, has client, and edict_t inuse.
//======================================================
qboolean G_EntExists(edict_t *ent) {
return ((ent) && (ent->client) && (ent->inuse));
}

//======================================================
// True if ent is not DEAD or DEAD or DEAD (and BURIED!)
//======================================================
qboolean G_ClientNotDead(edict_t *ent) {
qboolean buried=true;
qboolean b1=ent->client->ps.pmove.pm_type!=PM_DEAD;
qboolean b2=ent->deadflag != DEAD_DEAD;
qboolean b3=ent->health > 0;
return (b3||b2||b1)&&(buried);
}

//======================================================
// True if ent is not DEAD and not just did a Respawn.
//======================================================
qboolean G_ClientInGame(edict_t *ent) {
if (!G_EntExists(ent)) return false;
if (!G_ClientNotDead(ent)) return false;
return (ent->client->respawn_time + 5.0 < level.time);
}

//=====================================================
//=========== FLAGGING BEST PLAYER ROUTINES ===========
//=====================================================

//=========================================================
// Returns Player with Highest Score.
//=========================================================
edict_t *BestScoreEnt(void) {
edict_t *bestplayer=NULL;
int i, bestscore=-999;
edict_t *ent;

// Search thru all clients
for(i=0;i < game.maxclients; i++) {
ent=g_edicts+i+1;
if (!G_EntExists(ent)) continue;
if (ent->client->resp.score > bestscore) {
bestplayer=ent; // Found one!
bestscore=ent->client->resp.score; }
} // end for

return bestplayer;
}

//======================================================
// Ghost think routine.
//======================================================
void Ghost_Think(edict_t *Ghost) {
vec3_t start=(0,0,0);
vec3_t up;
edict_t *fPlayer=NULL;

// Unlink Ghost if intermission..
if (level.intermissiontime >= level.time) {
gi.unlinkentity(Ghost);
return; }

// Check Scoreboard every 1 sec.
if (Ghost->delay <= level.time) {
fPlayer=BestScoreEnt();
Ghost->delay =level.time + 1.0; }

// Move Ghost above fPlayer.
if ((fPlayer!=NULL) && G_ClientInGame(fPlayer)) {
Ghost->svflags &= ~SVF_NOCLIENT; // Turn ON Ghost
AngleVectors(fPlayer->s.angles, NULL, NULL, up);
VectorMA(fPlayer->s.origin, 60, up, start);
VectorCopy(start, Ghost->s.origin); // Move Ghost
gi.linkentity(Ghost); } // Must update Ghost!
else
// No fPlayer to follow so..
// turn OFF Ghost temporarily

Ghost->nextthink = level.time + 0.1; // Every frame..
}

//======================================================
// Create Ghost Entity
//======================================================
void Create_Ghost(void) {
edict_t *Ghost;

Ghost=G_Spawn();
Ghost->owner=NULL;
Ghost->s.effects=EF_TELEPORTER; // Particle effect.
Ghost->model="";
Ghost->takedamage=DAMAGE_NO;
Ghost->movetype=MOVETYPE_NONE;
Ghost->solid=SOLID_NOT;
Ghost->s.modelindex = 0;
Ghost->s.modelindex2 = 0;
VectorClear(Ghost->mins);
VectorClear(Ghost->maxs);
Ghost->delay=0;
Ghost->think=Ghost_Think;
Ghost->nextthink = 1.0; // Start in 1 second.
gi.linkentity(Ghost);
}

===============================================================
Lastly..

Be sure to add the following to your Autoexec.cfg file as
shown by example..

set sv_bestplayer 1

That's it!! Now get out there and KILL THE BASTARD WITH
THE HIGHEST SCORE!!!

And if its you, everybody is gonna be after your ass!!

Have Fun!!

philip