/*
 * 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
}
