1015 lines
32 KiB
C
1015 lines
32 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"
|
||
|
|
||
|
#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];
|
||
|
int lines_into_section = -1;
|
||
|
|
||
|
config_file = fopen(GAMEVERSION "/action.ini", "r");
|
||
|
if (config_file == NULL)
|
||
|
{
|
||
|
gi.dprintf("Unable to read %s\n", GAMEVERSION "/action.ini");
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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://action.telefragged.com/\n"
|
||
|
"\n");
|
||
|
lines = 3;
|
||
|
|
||
|
// 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 || !(ir->value))
|
||
|
{
|
||
|
char grenade_num[32];
|
||
|
if (tgren->value)
|
||
|
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 && !(ir->value)) ? " & " : "",
|
||
|
tgren->value ? 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++;
|
||
|
}
|
||
|
|
||
|
// 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++;
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
strcat(msg_buf, "\n");
|
||
|
lines++;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Insert blank line & action/motd.txt contents (whole MOTD gets truncated after 30 lines)
|
||
|
*/
|
||
|
|
||
|
if (motd_num_lines)
|
||
|
{
|
||
|
strcat(msg_buf, "\n");
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gi.centerprintf(ent, msg_buf);
|
||
|
}
|
||
|
|
||
|
// stuffcmd: forces a player to execute a command.
|
||
|
void stuffcmd(edict_t *ent, char *c)
|
||
|
{
|
||
|
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, vec3_t point, vec3_t direct)
|
||
|
{
|
||
|
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 (point, decal->s.origin);
|
||
|
|
||
|
vectoangles (direct, 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);
|
||
|
|
||
|
}
|
||
|
|
||
|
void SplatDie(edict_t *self)
|
||
|
{
|
||
|
G_FreeEdict(self);
|
||
|
}
|
||
|
|
||
|
void AddSplat (edict_t *self, vec3_t point, vec3_t direct)
|
||
|
{
|
||
|
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 (direct, 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);
|
||
|
|
||
|
}
|
||
|
|
||
|
/* %-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
|
||
|
}
|