quake2-action/a_game.c

1065 lines
35 KiB
C

/*
* a_game.c
* General game code for Action (formerly Axshun).
*
* Zucchini (spikard@u.washington.edu) and Fireblade (ucs_brf@shsu.edu)
* (splat/bullethole/shell ejection code from original Action source)
*/
#include "g_local.h"
#include "cgf_sfx_glass.h"
#define MAX_MAP_ROTATION 1000 // just in case...
#define MAX_STR_LEN 1000
#define MAX_TOTAL_MOTD_LINES 30
char team1_name[MAX_STR_LEN];
char team2_name[MAX_STR_LEN];
char team1_skin[MAX_STR_LEN];
char team2_skin[MAX_STR_LEN];
char team1_skin_index[MAX_STR_LEN + 30];
char team2_skin_index[MAX_STR_LEN + 30];
char *map_rotation[MAX_MAP_ROTATION];
int num_maps, cur_map;
char motd_lines[MAX_TOTAL_MOTD_LINES][70];
int motd_num_lines;
/*
* ReadConfigFile()
* Config file format is backwards compatible with Action's, but doesn't need
* the "###" designator at end of sections.
* -Fireblade
*/
void ReadConfigFile()
{
FILE *config_file;
char buf[MAX_STR_LEN];
char reading_section[MAX_STR_LEN];
char inipath[MAX_STR_LEN];
int lines_into_section = -1;
cvar_t *ininame;
ininame = gi.cvar("ininame", "action.ini", 0);
if (ininame->string && *(ininame->string))
sprintf(inipath, "%s/%s", GAMEVERSION, ininame->string);
else
sprintf(inipath, "%s/%s", GAMEVERSION, "action.ini");
config_file = fopen(inipath, "r");
if (config_file == NULL)
{
gi.dprintf("Unable to read %s\n", inipath);
return;
}
while (fgets(buf, MAX_STR_LEN - 10, config_file) != NULL)
{
int bs;
bs = strlen(buf);
while (buf[bs-1] == '\r' || buf[bs-1] == '\n')
{
buf[bs-1] = 0;
bs--;
}
if ((buf[0] == '/' && buf[1] == '/') || buf[0] == 0)
{
continue;
}
if (buf[0] == '[')
{
char *p;
p = strchr(buf, ']');
if (p == NULL)
continue;
*p = 0;
strcpy(reading_section, buf + 1);
lines_into_section = 0;
continue;
}
if (buf[0] == '#' && buf[1] == '#' && buf[2] == '#')
{
lines_into_section = -1;
continue;
}
if (lines_into_section > -1)
{
if (!strcmp(reading_section, "team1"))
{
if (lines_into_section == 0)
{
strcpy(team1_name, buf);
}
else if (lines_into_section == 1)
{
strcpy(team1_skin, buf);
}
}
else if (!strcmp(reading_section, "team2"))
{
if (lines_into_section == 0)
{
strcpy(team2_name, buf);
}
else if (lines_into_section == 1)
{
strcpy(team2_skin, buf);
}
}
else if (!strcmp(reading_section, "maplist"))
{
map_rotation[num_maps] = (char *)gi.TagMalloc(strlen(buf) + 1, TAG_GAME);
strcpy(map_rotation[num_maps], buf);
num_maps++;
}
lines_into_section++;
}
}
sprintf(team1_skin_index, "../players/%s_i", team1_skin);
sprintf(team2_skin_index, "../players/%s_i", team2_skin);
cur_map = 0;
fclose(config_file);
}
void ReadMOTDFile()
{
FILE *motd_file;
char buf[1000];
int lbuf;
motd_file = fopen(GAMEVERSION "/motd.txt", "r");
if (motd_file == NULL)
return;
motd_num_lines = 0;
while (fgets(buf, 900, motd_file) != NULL)
{
lbuf = strlen(buf);
while (buf[lbuf-1] == '\r' || buf[lbuf-1] == '\n')
{
buf[lbuf-1] = 0;
lbuf--;
}
if (lbuf > 40)
buf[40] = 0;
strcpy(motd_lines[motd_num_lines], buf);
motd_num_lines++;
if (motd_num_lines >= MAX_TOTAL_MOTD_LINES)
break;
}
fclose(motd_file);
}
void PrintMOTD(edict_t *ent)
{
int mapnum, i, lines = 0;
int max_lines = MAX_TOTAL_MOTD_LINES;
char msg_buf[16384], *server_type;
/*
* Welcome message
*/
// 3 lines for version number & website (third blank)
strcpy(msg_buf, "Welcome to Action Quake v" ACTION_VERSION "\n"
"http://aq2.action-web.net/\n"
"\n");
lines = 3;
if (!skipmotd->value)
{
// Line for server hostname, if set
if (hostname->string && strlen(hostname->string) && strcmp(hostname->string, "unnamed"))
{
//strcat(msg_buf, "Running at: ");
strcat(msg_buf, hostname->string);
strcat(msg_buf, "\n");
lines++;
}
/*
* Server-specific settings information
*/
// Line for game type
if (teamplay->value)
{
server_type = "teamplay";
}
else
{
if ((int)dmflags->value & DF_MODELTEAMS)
server_type = "deathmatch (teams by model)";
else if ((int)dmflags->value & DF_SKINTEAMS)
server_type = "deathmatch (teams by skin)";
else
server_type = "deathmatch (no teams)";
}
sprintf(msg_buf + strlen(msg_buf),
"Game type: %s\n",
server_type);
lines++;
// Line for frag and time limit
if ((int)fraglimit->value)
sprintf(msg_buf + strlen(msg_buf), "Frag limit: %d", (int)fraglimit->value);
else
strcat(msg_buf, "Frag limit: none");
if ((int)timelimit->value)
sprintf(msg_buf + strlen(msg_buf), " Time limit: %d\n", (int)timelimit->value);
else
strcat(msg_buf, " Time limit: none\n");
lines++;
// Teamplay: 3 lines for round limit/round time limit, and menu instructions
if (teamplay->value)
{
if ((int)roundlimit->value)
sprintf(msg_buf + strlen(msg_buf), "Round limit: %d", (int)roundlimit->value);
else
strcat(msg_buf, "Round limit: none");
if ((int)roundtimelimit->value)
sprintf(msg_buf + strlen(msg_buf), " Round time limit: %d\n", (int)roundtimelimit->value);
else
strcat(msg_buf, " Round time limit: none\n");
lines++;
}
// Check for weird unique items/weapons settings, and inform with a line
if ((int)unique_weapons->value != 1 ||
(int)unique_items->value != 1)
{
sprintf(msg_buf + strlen(msg_buf), "Max carry of special weaps: %d items: %d\n",
(int)unique_weapons->value, (int)unique_items->value);
lines++;
}
if (tgren->value > 0 || !(ir->value))
{
char grenade_num[32];
if (tgren->value > 0)
sprintf(grenade_num, "%d grenade%s",
(int)tgren->value,
(int)tgren->value == 1 ? "" : "s");
sprintf(msg_buf + strlen(msg_buf), "Bandolier w/ %s%s%s\n",
!(ir->value) ? "no IR" : "",
(tgren->value > 0 && !(ir->value)) ? " & " : "",
tgren->value > 0 ? grenade_num : "");
lines++;
}
if (allitem->value || allweapon->value)
{
sprintf(msg_buf + strlen(msg_buf), "Players receive %s%s%s\n",
allweapon->value ? "all weapons" : "",
(allweapon->value && allitem->value) ? " & " : "",
allitem->value ? "all items" : "");
lines++;
}
if (limchasecam->value)
{
if ((int)limchasecam->value == 2)
sprintf(msg_buf + strlen(msg_buf), "Chase cam disallowed\n");
else
sprintf(msg_buf + strlen(msg_buf), "Chase cam restricted\n");
lines++;
}
if (teamplay->value && !((int)dmflags->value & DF_NO_FRIENDLY_FIRE))
{
sprintf(msg_buf + strlen(msg_buf), "Friendly fire enabled\n");
lines++;
}
// Teamplay: 2 lines (one blank) for menu instructions
if (teamplay->value)
{
strcat(msg_buf, "\nHit tab to open the team selection menu\n");
lines += 2;
}
/*
* If actionmaps, put a blank line then the maps list
*/
// hopefully no one will run enough maps to exceed the line limit, if so, oh well... -FB
if (actionmaps->value && num_maps > 0)
{
int chars_on_line, len_mr;
strcat(msg_buf, "\nRunning the following maps in order:\n");
lines += 2;
chars_on_line = 0;
for (mapnum = 0; mapnum < num_maps; mapnum++)
{
len_mr = strlen(*(map_rotation + mapnum));
if ((chars_on_line + len_mr + 2) > 39)
{
strcat(msg_buf, "\n");
lines++;
if (lines >= max_lines)
break;
chars_on_line = 0;
}
strcat(msg_buf, *(map_rotation + mapnum));
chars_on_line += len_mr;
if (mapnum < (num_maps - 1))
{
strcat(msg_buf, ", ");
chars_on_line += 2;
}
}
if (lines < max_lines)
{
strcat(msg_buf, "\n");
lines++;
}
}
if (motd_num_lines && lines < max_lines)
{
strcat(msg_buf, "\n");
lines++;
}
}
/*
* Insert action/motd.txt contents (whole MOTD gets truncated after 30 lines)
*/
if (motd_num_lines)
{
if (lines < max_lines)
{
for (i = 0; i < motd_num_lines; i++)
{
strcat(msg_buf, motd_lines[i]);
strcat(msg_buf, "\n");
lines++;
if (lines >= max_lines)
break;
}
}
}
safe_centerprintf(ent, msg_buf);
}
// stuffcmd: forces a player to execute a command.
void stuffcmd(edict_t *ent, char *c)
{
// ACEBOT ADD
if( !stricmp( ent->classname,"bot"))
return;
// ACEBOT END
gi.WriteByte(svc_stufftext);
gi.WriteString(c);
gi.unicast(ent, true);
}
/********************************************************************************
*
* zucc: following are EjectBlooder, EjectShell, AddSplat, and AddDecal
* code. All from actionquake, some had to be modified to fit Axshun or fix
* bugs.
*
*/
int decals = 0;
int shells = 0;
int splats = 0;
//blooder used for bleeding
void BlooderDie(edict_t *self)
{
G_FreeEdict(self);
}
void BlooderTouch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
G_FreeEdict(self);
}
void EjectBlooder (edict_t *self, vec3_t start, vec3_t veloc )
{
edict_t *blooder;
vec3_t forward;
int spd = 0;
blooder = G_Spawn();
VectorCopy (veloc, forward);
VectorCopy (start , blooder->s.origin);
spd = 0;
VectorScale (forward, spd, blooder->velocity);
blooder->solid = SOLID_NOT;
blooder->movetype = MOVETYPE_TOSS;
blooder->s.modelindex = gi.modelindex("sprites/null.sp2");
blooder->s.effects |= EF_GIB;
blooder->owner = self;
blooder->touch = BlooderTouch;
blooder->nextthink = level.time + 3.2;
blooder->think = BlooderDie;
blooder->classname = "blooder";
gi.linkentity (blooder);
}
// zucc - Adding EjectShell code from action quake, modified for Axshun.
/********* SHELL EJECTION **************/
void ShellTouch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (self->owner->client->curr_weap == M3_NUM)
gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/shellhit1.wav"), 1, ATTN_STATIC, 0);
else if (random() < 0.5)
gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_STATIC, 0);
else
gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink2.wav"), 1, ATTN_STATIC, 0);
}
void ShellDie(edict_t *self)
{
G_FreeEdict(self);
--shells;
}
// zucc fixed this so it works with the sniper rifle and checks handedness
// had to add the toggle feature to handle the akimbos correctly, if 1
// it sets up for ejecting the shell from the left akimbo weapon, if 2
// it fires right handed akimbo
void EjectShell (edict_t *self, vec3_t start, int toggle )
{
edict_t *shell;
vec3_t forward, right, up;
float r;
float fix = 1.0;
int left = 0;
if (sv_shelloff->value)
return;
shell = G_Spawn();
++shells;
AngleVectors (self->client->v_angle, forward, right, up);
if (self->client->pers.hand == LEFT_HANDED)
{
left = 1;
fix = -1.0;
}
else if ( self->client->pers.hand == CENTER_HANDED)
fix = 0;
// zucc spent a fair amount of time hacking these until they look ok,
// several of them could be improved however.
if (self->client->curr_weap == MK23_NUM )
{
VectorMA (start, left?-7:.4, right, start);
VectorMA (start, left?5:2, forward, start);
VectorMA (start, left?-10:-8 , up, start);
}
else if (self->client->curr_weap == M4_NUM)
{
VectorMA (start, left?-10:5, right, start);
VectorMA (start, left?6:12, forward, start);
VectorMA (start, left?-9:-11, up, start);
}
else if (self->client->curr_weap == MP5_NUM)
{
VectorMA (start, left?-10:6, right, start);
VectorMA (start, left?6:8, forward, start);
VectorMA (start, left?-9:-10, up, start);
}
else if (self->client->curr_weap == SNIPER_NUM)
{
VectorMA (start, fix*11, right, start);
VectorMA (start, 2, forward, start);
VectorMA (start, -11, up, start);
}
else if (self->client->curr_weap == M3_NUM)
{
VectorMA(start, left?-9:3, right, start);
VectorMA(start, left?4:4, forward, start);
VectorMA(start, left?-1:-1, up, start);
}
else if (self->client->curr_weap == DUAL_NUM)
{
if (self->client->pers.hand == LEFT_HANDED)
VectorMA (start, ((toggle==1)?8:-8), right, start);
else
VectorMA (start, ((toggle==1)?-4:4), right, start);
VectorMA (start, 6, forward, start);
VectorMA (start, -9, up, start);
}
if ( (forward[2] >= -1) && (forward[2] < -0.99) ) {
VectorMA (start, 5, forward, start);
VectorMA (start, -0.5, up, start); }
else if ( (forward[2] >= -0.99) && (forward[2] < -0.98) ) {
VectorMA (start, 5, forward, start);
VectorMA (start, -.1, up, start); }
else if ( (forward[2] >= -0.98) && (forward[2] < -0.97) ) {
VectorMA (start, 5.1, forward, start);
VectorMA (start, 0.3, up, start); }
else if ( (forward[2] >= -0.97) && (forward[2] < -0.96) ) {
VectorMA (start, 5.2, forward, start);
VectorMA (start, 0.7, up, start); }
else if ( (forward[2] >= -0.96) && (forward[2] < -0.95) ) {
VectorMA (start, 5.2, forward, start);
VectorMA (start, 1.1, up, start); }
else if ( (forward[2] >= -0.95) && (forward[2] < -0.94) ) {
VectorMA (start, 5.3, forward, start);
VectorMA (start, 1.5, up, start); }
else if ( (forward[2] >= -0.94) && (forward[2] < -0.93) ) {
VectorMA (start, 5.4, forward, start);
VectorMA (start, 1.9, up, start); }
else if ( (forward[2] >= -0.93) && (forward[2] < -0.92) ) {
VectorMA (start, 5.5, forward, start);
VectorMA (start, 2.3, up, start); }
else if ( (forward[2] >= -0.92) && (forward[2] < -0.91) ) {
VectorMA (start, 5.6, forward, start);
VectorMA (start, 2.7, up, start); }
else if ( (forward[2] >= -0.91) && (forward[2] < -0.9) ) {
VectorMA (start, 5.7, forward, start);
VectorMA (start, 3.1, up, start); }
else if ( (forward[2] >= -0.9) && (forward[2] < -0.85) ) {
VectorMA (start, 5.8, forward, start);
VectorMA (start, 3.5, up, start); }
else if ( (forward[2] >= -0.85) && (forward[2] < -0.8) ) {
VectorMA (start, 6, forward, start);
VectorMA (start, 4, up, start); }
else if ( (forward[2] >= -0.8) && (forward[2] < -0.6) ) {
VectorMA (start, 6.5, forward, start);
VectorMA (start, 4.5, up , start); }
else if ( (forward[2] >= -0.6) && (forward[2] < -0.4) ) {
VectorMA (start, 8, forward, start);
VectorMA (start, 5.5, up , start); }
else if ( (forward[2] >= -0.4) && (forward[2] < -0.2) ) {
VectorMA (start, 9.5, forward, start);
VectorMA (start, 6, up , start); }
else if ( (forward[2] >= -0.2) && (forward[2] < 0) ) {
VectorMA (start, 11, forward, start);
VectorMA (start, 6.5, up , start); }
else if ( (forward[2] >= 0) && (forward[2] < 0.2) ) {
VectorMA (start, 12, forward, start);
VectorMA (start, 7, up, start); }
else if ( (forward[2] >= 0.2) && (forward[2] < 0.4) ) {
VectorMA (start, 14, forward, start);
VectorMA (start, 6.5, up, start); }
else if ( (forward[2] >= 0.4) && (forward[2] < 0.6) ) {
VectorMA (start, 16, forward, start);
VectorMA (start, 6, up, start); }
else if ( (forward[2] >= 0.6) && (forward[2] < 0.8) ) {
VectorMA (start, 18, forward, start);
VectorMA (start, 5, up, start); }
else if ( (forward[2] >= 0.8) && (forward[2] < 0.85) ) {
VectorMA (start, 18, forward, start);
VectorMA (start, 4, up, start); }
else if ( (forward[2] >= 0.85) && (forward[2] < 0.9) ) {
VectorMA (start, 18, forward, start);
VectorMA (start, 2.5, up, start); }
else if ( (forward[2] >= 0.9) && (forward[2] < 0.91) ) {
VectorMA (start, 18.2, forward, start);
VectorMA (start, 2.2, up, start); }
else if ( (forward[2] >= 0.91) && (forward[2] < 0.92) ) {
VectorMA (start, 18.4, forward, start);
VectorMA (start, 1.9, up, start); }
else if ( (forward[2] >= 0.92) && (forward[2] < 0.93) ) {
VectorMA (start, 18.6, forward, start);
VectorMA (start, 1.6, up, start); }
else if ( (forward[2] >= 0.93) && (forward[2] < 0.94) ) {
VectorMA (start, 18.8, forward, start);
VectorMA (start, 1.3, up, start); }
else if ( (forward[2] >= 0.94) && (forward[2] < 0.95) ) {
VectorMA (start, 19, forward, start);
VectorMA (start, 1, up, start); }
else if ( (forward[2] >= 0.95) && (forward[2] < 0.96) ) {
VectorMA (start, 19.2, forward, start);
VectorMA (start, 0.7, up, start); }
else if ( (forward[2] >= 0.96) && (forward[2] < 0.97) ) {
VectorMA (start, 19.4, forward, start);
VectorMA (start, 0.4, up, start); }
else if ( (forward[2] >= 0.97) && (forward[2] < 0.98) ) {
VectorMA (start, 19.6, forward, start);
VectorMA (start, -0.2, up, start); }
else if ( (forward[2] >= 0.98) && (forward[2] < 0.99) ) {
VectorMA (start, 19.8, forward, start);
VectorMA (start, -0.6, up, start); }
else if ( (forward[2] >= 0.99) && (forward[2] <= 1) ) {
VectorMA (start, 20, forward, start);
VectorMA (start, -1, up , start); }
VectorCopy (start , shell->s.origin);
if (fix == 0) // we want some velocity on those center handed ones
fix = 1;
if (self->client->curr_weap == SNIPER_NUM)
VectorMA (shell->velocity, fix*(-35 + random() * -60), right, shell->velocity);
else if ( self->client->curr_weap == DUAL_NUM)
{
if (self->client->pers.hand == LEFT_HANDED)
VectorMA (shell->velocity, (toggle==1?1:-1)*(35 + random() * 60), right, shell->velocity);
else
VectorMA (shell->velocity, (toggle==1?-1:1)*(35 + random() * 60), right, shell->velocity);
}
else
VectorMA (shell->velocity, fix*(35 + random() * 60), right, shell->velocity);
VectorMA (shell->avelocity, 500, right, shell->avelocity);
if (self->client->curr_weap == SNIPER_NUM)
VectorMA (shell->velocity, 60 + 40, up, shell->velocity);
else
VectorMA (shell->velocity, 60 + random() * 90, up, shell->velocity);
shell->movetype = MOVETYPE_BOUNCE;
shell->solid = SOLID_BBOX;
if (self->client->curr_weap == M3_NUM)
shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris2.md2");
else if (self->client->curr_weap == SNIPER_NUM)
shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris3.md2");
else
shell->s.modelindex = gi.modelindex ("models/weapons/shell/tris.md2");
r = random();
if (r < 0.1)
shell->s.frame = 0;
else if (r < 0.2)
shell->s.frame = 1;
else if (r < 0.3)
shell->s.frame = 2;
else if (r < 0.5)
shell->s.frame = 3;
else if (r < 0.6)
shell->s.frame = 4;
else if (r < 0.7)
shell->s.frame = 5;
else if (r < 0.8)
shell->s.frame = 6;
else if (r < 0.9)
shell->s.frame = 7;
else
shell->s.frame = 8;
shell->owner = self;
shell->touch = ShellTouch;
shell->nextthink = level.time + 1.2 - (shells * .05);
shell->think = ShellDie;
shell->classname = "shell";
gi.linkentity (shell);
}
/*
==================
FindEdictByClassnum
==================
*/
edict_t *FindEdictByClassnum (char *classname, int classnum)
{
int i;
edict_t *it;
for (i=0 ; i<globals.num_edicts ; i++)
{
it = &g_edicts[i];
if (!it->classname)
continue;
if (!it->classnum)
continue;
if (Q_stricmp(it->classname, classname) == 0)
{
if (it->classnum == classnum)
return it;
}
}
return NULL;
}
/********* Bulletholes/wall stuff ***********/
void DecalDie(edict_t *self)
{
G_FreeEdict(self);
}
void AddDecal (edict_t *self, trace_t* tr)
{
edict_t *decal;
edict_t *dec;
if (bholelimit->value < 1)
return;
decal = G_Spawn();
++decals;
if (decals > bholelimit->value)
decals = 1;
dec = FindEdictByClassnum ("decal", decals);
if (dec)
{
// gi.bprintf (PRINT_HIGH, "Decal->classnum == %i\n", dec->classnum);
dec->nextthink = level.time + .1;
}
decal->solid = SOLID_NOT;
decal->movetype = MOVETYPE_NONE;
decal->s.modelindex = gi.modelindex("models/objects/holes/hole1/hole.md2");
VectorCopy (tr->endpos, decal->s.origin);
vectoangles (tr->plane.normal, decal->s.angles);
decal->owner = self;
decal->classnum = decals;
decal->touch = NULL;
decal->nextthink = level.time + 20;
decal->think = DecalDie;
decal->classname = "decal";
gi.linkentity (decal);
//glass
if ((tr->ent) && (0 == Q_stricmp("func_explosive", tr->ent->classname)))
{
CGF_SFX_AttachDecalToGlass(tr->ent, decal);
}
//---
}
void SplatDie(edict_t *self)
{
G_FreeEdict(self);
}
void AddSplat (edict_t *self, vec3_t point, trace_t* tr)
{
edict_t *splat;
edict_t *spt;
float r;
if (splatlimit->value < 1)
return;
splat = G_Spawn();
++splats;
if (splats > splatlimit->value)
splats = 1;
spt = FindEdictByClassnum ("splat", splats);
if (spt)
{
// gi.bprintf (PRINT_HIGH, "Decal->classnum == %i\n", dec->classnum);
spt->nextthink = level.time + .1;
}
splat->solid = SOLID_NOT;
splat->movetype = MOVETYPE_NONE;
r = random();
if ( r > .67 )
splat->s.modelindex = gi.modelindex("models/objects/splats/splat1/splat.md2");
else if ( r > .33 )
splat->s.modelindex = gi.modelindex("models/objects/splats/splat2/splat.md2");
else
splat->s.modelindex = gi.modelindex("models/objects/splats/splat3/splat.md2");
VectorCopy (point, splat->s.origin);
vectoangles (tr->plane.normal, splat->s.angles);
splat->owner = self;
splat->touch = NULL;
splat->nextthink = level.time + 25;// - (splats * .05);
splat->think = SplatDie;
splat->classname = "splat";
splat->classnum = splats;
gi.linkentity (splat);
//glass
if ((tr->ent) && (0 == Q_stricmp("func_explosive", tr->ent->classname)))
{
CGF_SFX_AttachDecalToGlass(tr->ent, splat);
}
//---
}
/* %-variables for chat msgs */
void GetWeaponName(edict_t *ent, char *buf)
{
if (ent->client->pers.weapon)
{
strcpy(buf, ent->client->pers.weapon->pickup_name);
return;
}
strcpy(buf, "No Weapon");
}
void GetItemName(edict_t *ent, char *buf)
{
gitem_t *spec;
int i;
i = 0;
while (tnames[i])
{
if ((spec = FindItemByClassname(tnames[i])) != NULL &&
ent->client->pers.inventory[ITEM_INDEX(spec)])
{
strcpy(buf, spec->pickup_name);
return;
}
i++;
}
strcpy(buf, "No Item");
}
void GetHealth(edict_t *ent, char *buf)
{
sprintf(buf, "%d", ent->health);
}
void GetAmmo(edict_t *ent, char *buf)
{
int ammo;
if (ent->client->pers.weapon)
{
switch (ent->client->curr_weap)
{
case MK23_NUM:
sprintf(buf, "%d rounds (%d extra clips)",
ent->client->mk23_rds,
ent->client->pers.inventory[ent->client->ammo_index]);
return;
case MP5_NUM:
sprintf(buf, "%d rounds (%d extra clips)",
ent->client->mp5_rds,
ent->client->pers.inventory[ent->client->ammo_index]);
return;
case M4_NUM:
sprintf(buf, "%d rounds (%d extra clips)",
ent->client->m4_rds,
ent->client->pers.inventory[ent->client->ammo_index]);
return;
case M3_NUM:
sprintf(buf, "%d shells (%d extra shells)",
ent->client->shot_rds,
ent->client->pers.inventory[ent->client->ammo_index]);
return;
case HC_NUM:
sprintf(buf, "%d shells (%d extra shells)",
ent->client->cannon_rds,
ent->client->pers.inventory[ent->client->ammo_index]);
return;
case SNIPER_NUM:
sprintf(buf, "%d rounds (%d extra rounds)",
ent->client->sniper_rds,
ent->client->pers.inventory[ent->client->ammo_index]);
return;
case DUAL_NUM:
sprintf(buf, "%d rounds (%d extra clips)",
ent->client->dual_rds,
ent->client->pers.inventory[ent->client->ammo_index]);
return;
case KNIFE_NUM:
ammo = ent->client->pers.inventory[ITEM_INDEX(FindItem(KNIFE_NAME))];
sprintf(buf, "%d kni%s", ammo, (ammo == 1) ? "fe" : "ves");
return;
case GRENADE_NUM:
ammo = ent->client->pers.inventory[ITEM_INDEX(FindItem(GRENADE_NAME))];
sprintf(buf, "%d grenade%s", ammo, (ammo == 1) ? "" : "s");
return;
}
}
strcpy(buf, "no ammo");
}
void GetNearbyTeammates(edict_t *self, char *buf)
{
unsigned char nearby_teammates[10][16];
int nearby_teammates_num, l;
edict_t *ent = NULL;
nearby_teammates_num = 0;
while ((ent = findradius(ent, self->s.origin, 1500)) != NULL)
{
if (ent == self || !ent->client || !CanDamage(ent, self) ||
!OnSameTeam(ent, self))
continue;
strncpy(nearby_teammates[nearby_teammates_num], ent->client->pers.netname, 15);
nearby_teammates[nearby_teammates_num][15] = 0; // in case their name is 15 chars...
nearby_teammates_num++;
if (nearby_teammates_num >= 10)
break;
}
if (nearby_teammates_num == 0)
{
strcpy(buf, "nobody");
return;
}
for (l = 0; l < nearby_teammates_num; l++)
{
if (l == 0)
{
strcpy(buf, nearby_teammates[l]);
}
else
{
if (nearby_teammates_num == 2)
{
strcat(buf, " and ");
strcat(buf, nearby_teammates[l]);
}
else
{
if (l == (nearby_teammates_num - 1))
{
strcat(buf, ", and ");
strcat(buf, nearby_teammates[l]);
}
else
{
strcat(buf, ", ");
strcat(buf, nearby_teammates[l]);
}
}
}
}
}
char *SeekBufEnd(char *buf)
{
while (*buf != 0)
buf++;
return buf;
}
void ParseSayText(edict_t *ent, char *text)
{
static unsigned char buf[10240], infobuf[10240];
char *p, *pbuf;
p = text;
pbuf = buf;
*pbuf = 0;
while (*p != 0)
{
if (((ptrdiff_t)pbuf - (ptrdiff_t)buf) > 300)
{
break;
}
if (*p == '%')
{
switch (*(p+1))
{
case 'H':
GetHealth(ent, infobuf);
strcpy(pbuf, infobuf);
pbuf = SeekBufEnd(pbuf);
p += 2;
continue;
case 'A':
GetAmmo(ent, infobuf);
strcpy(pbuf, infobuf);
pbuf = SeekBufEnd(pbuf);
p += 2;
continue;
case 'W':
GetWeaponName(ent, infobuf);
strcpy(pbuf, infobuf);
pbuf = SeekBufEnd(pbuf);
p += 2;
continue;
case 'I':
GetItemName(ent, infobuf);
strcpy(pbuf, infobuf);
pbuf = SeekBufEnd(pbuf);
p += 2;
continue;
case 'T':
GetNearbyTeammates(ent, infobuf);
strcpy(pbuf, infobuf);
pbuf = SeekBufEnd(pbuf);
p += 2;
continue;
}
}
*pbuf++ = *p++;
}
*pbuf = 0;
strncpy(text, buf, 300);
text[300] = 0; // in case it's 300
}