// zucc
// File for our new commands.

// laser sight patch, by Geza Beladi

#include "g_local.h"



extern void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);


/*----------------------------------------
SP_LaserSight

  Create/remove the laser sight entity
-----------------------------------------*/

#define lss self->lasersight

void SP_LaserSight(edict_t *self, gitem_t *item ) {
        
        vec3_t  start,forward,right,end;
        int laser_on = 0;
        gitem_t *temp_item;
        


//      gi.cprintf(self, PRINT_HIGH, "Calling lasersight function have_laser %d.\n", self->client->have_laser);

        temp_item = FindItem(LASER_NAME);
        if ( !(self->client->pers.inventory[ITEM_INDEX(temp_item)]) )
        {
                if ( lss ) // laser is on
                {
                        G_FreeEdict(lss);
                        lss = NULL;
                }
//              gi.cprintf(self, PRINT_HIGH, "didn't have laser sight.\n");             
                return;
        }

        // zucc code to make it be used with the right weapons
        

//      gi.cprintf(self, PRINT_HIGH, "curr_weap = %d.\n", self->client->curr_weap);             

        switch ( self->client->curr_weap )
        {
        case MK23_NUM:
        case MP5_NUM:
        case M4_NUM:
                {
                        laser_on = 1;
                        break;
                }
        default:
                {
                        laser_on = 0;
                        break;
                }
        }

//      gi.cprintf(self, PRINT_HIGH, "laser_on is %d.\n", laser_on);            

        // laser is on but we want it off
        if ( lss && !laser_on ) {
//              gi.cprintf(self, PRINT_HIGH, "trying to free the laser sight\n");               
                G_FreeEdict(lss);
                lss = NULL;
                //gi.bprintf (PRINT_HIGH, "lasersight off.");
                return;
        }
        
//      gi.cprintf(self, PRINT_HIGH, "laser wasn't lss is %p.\n", lss);         

        // off and we want it to stay that way
        if ( !laser_on )
                return;

        //gi.bprintf (PRINT_HIGH, "lasersight on.");
        
        AngleVectors (self->client->v_angle, forward, right, NULL);
        
        VectorSet(end,100 , 0, 0);
        G_ProjectSource (self->s.origin, end, forward, right, start);
        
        lss = G_Spawn ();
        lss->owner = self;
        lss->movetype = MOVETYPE_NOCLIP;
        lss->solid = SOLID_NOT;
        lss->classname = "lasersight";
        lss->s.modelindex = gi.modelindex ("sprites/lsight.sp2");
        lss->s.renderfx = RF_TRANSLUCENT;
        lss->think = LaserSightThink;
        lss->nextthink = level.time + 0.01;
}


/*---------------------------------------------
LaserSightThink

  Updates the sights position, angle, and shape
  is the lasersight entity
---------------------------------------------*/

void LaserSightThink (edict_t *self)
{
        vec3_t start,end,endp,offset;
        vec3_t forward,right,up;
        trace_t tr;

        AngleVectors (self->owner->client->v_angle, forward, right, up);
        
        
        if ( self->owner->lasersight != self )
        {
                self->think = G_FreeEdict;
        }


        VectorSet(offset,24 , 0, self->owner->viewheight);
        
        P_ProjectSource (self->owner->client, self->owner->s.origin, offset, forward, right, start);
        VectorMA(start,8192,forward,end);
        
        tr = do_trace (start,NULL,NULL, end,self->owner,CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
        
        if (tr.fraction != 1) {
                VectorMA(tr.endpos,-4,forward,endp);
                VectorCopy(endp,tr.endpos);
        }
        
        vectoangles(tr.plane.normal,self->s.angles);
        VectorCopy(tr.endpos,self->s.origin);
        
        gi.linkentity (self);
        self->nextthink = level.time + 0.1;
}


//+BD ENTIRE CODE BLOCK NEW
// Cmd_Reload_f()
// Handles weapon reload requests
void Cmd_Reload_f (edict_t *ent)
{
//      int rds_left;           //+BD - Variable to handle rounds left


        //+BD - If the player is dead, don't bother
        if(ent->deadflag == DEAD_DEAD)
        {
                //gi.centerprintf(ent, "I know you're a hard ass,\nBUT YOU'RE FUCKING DEAD!!\n");
                return;
        }

        if(ent->client->weaponstate == WEAPON_BANDAGING
                                                || ent->client->bandaging == 1
                                                || ent->client->bandage_stopped == 1
                                                || ent->client->weaponstate == WEAPON_ACTIVATING
                                                || ent->client->weaponstate == WEAPON_DROPPING )
        {
                return;
        }
        

        //First, grab the current magazine max count...
        if ( ( ent->client->curr_weap == MK23_NUM  )
                || ( ent->client->curr_weap == MP5_NUM ) 
                || ( ent->client->curr_weap == M4_NUM ) 
                || ( ent->client->curr_weap == M3_NUM )
                || ( ent->client->curr_weap == HC_NUM )
                || ( ent->client->curr_weap == SNIPER_NUM )
                || ( ent->client->curr_weap == DUAL_NUM ) )
        {
        }
                else    //We should never get here, but...
                //BD 5/26 - Actually we get here quite often right now. Just exit for weaps that we
                //          don't want reloaded or that never reload (grenades)
        {
                //gi.centerprintf(ent,"Where'd you train?\nYou can't reload that!\n");
                return;
        }
        
        if(ent->client->pers.inventory[ent->client->ammo_index])
        {       
                /*if((ent->client->weaponstate != WEAPON_END_MAG) && (ent->client->pers.inventory[ent->client->ammo_index] < rds_left))
                {
                        gi.centerprintf(ent,"Buy a clue-\nYou're on your last magazine!\n");
                }
                else*/
                        //Set the weaponstate...
                if ( ent->client->curr_weap == M3_NUM )
                {
                        if (ent->client->shot_rds >= ent->client->shot_max)
                        {
                                return;
                        }
                        // already in the process of reloading!
                        if ( ent->client->weaponstate == WEAPON_RELOADING && (ent->client->shot_rds < (ent->client->shot_max -1)) && !(ent->client->fast_reload) && ((ent->client->pers.inventory[ent->client->ammo_index] -1) > 0 ))
                        {
                                ent->client->fast_reload = 1;
                                (ent->client->pers.inventory[ent->client->ammo_index])--;
                        }
                }                       
                if ( ent->client->curr_weap == HC_NUM )
                {
                                         if (ent->client->cannon_rds >= ent->client->cannon_max)
                                         {
                                                 return;
                                         }
                                         
                                         
                        if (!(ent->client->pers.inventory[ent->client->ammo_index] >= 2))
                                return;
                }
                if ( ent->client->curr_weap == SNIPER_NUM )
                {
                        if (ent->client->sniper_rds >= ent->client->sniper_max)
                        {
                                return;
                        }
                        // already in the process of reloading!
                        if ( ent->client->weaponstate == WEAPON_RELOADING && (ent->client->sniper_rds < (ent->client->sniper_max -1)) && !(ent->client->fast_reload) && ((ent->client->pers.inventory[ent->client->ammo_index] -1) > 0 ))
                        {
                                ent->client->fast_reload = 1;
                                (ent->client->pers.inventory[ent->client->ammo_index])--;
                        }
                        ent->client->ps.fov = 90;
                        if ( ent->client->pers.weapon )
                                ent->client->ps.gunindex = gi.modelindex( ent->client->pers.weapon->view_model );
        
                }                       
                if ( ent->client->curr_weap == DUAL_NUM )
                {
                        if (!(ent->client->pers.inventory[ent->client->ammo_index] >= 2))
                                return;
                }
                
                
                ent->client->weaponstate = WEAPON_RELOADING;
                //(ent->client->pers.inventory[ent->client->ammo_index])--;
        }
        else
                gi.cprintf (ent, PRINT_HIGH, "Out of ammo\n");
                //gi.centerprintf(ent,"Pull your head out-\nYou've got NO AMMO!\n");
}
//+BD END CODE BLOCK

// function to change the firing mode of weapons (when appropriate) 

void Cmd_Weapon_f ( edict_t *ent )
{
        int dead;

        dead = (ent->solid == SOLID_NOT || ent->deadflag == DEAD_DEAD);

        if ( ent->client->bandaging || ent->client->bandage_stopped )
        {
                gi.cprintf(ent, PRINT_HIGH, "You can't mess with your weapon while bandaging!\n");
                return;
        }

        if ( ent->client->curr_weap == MK23_NUM )
        {
                if (!dead)
                        gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/click.wav"), 1, ATTN_NORM, 0);
                ent->client->resp.mk23_mode = !(ent->client->resp.mk23_mode);
                if ( ent->client->resp.mk23_mode )
                        gi.cprintf (ent, PRINT_HIGH, "MK23 Pistol set for semi-automatic action\n");
                else
                        gi.cprintf (ent, PRINT_HIGH, "MK23 Pistol set for automatic action\n");
        }
        if ( ent->client->curr_weap == MP5_NUM )
        {
                if (!dead)
                        gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/click.wav"), 1, ATTN_NORM, 0);
                ent->client->resp.mp5_mode = !(ent->client->resp.mp5_mode);
                if ( ent->client->resp.mp5_mode )
                        gi.cprintf (ent, PRINT_HIGH, "MP5 set to 3 Round Burst mode\n");
                else
                        gi.cprintf (ent, PRINT_HIGH, "MP5 set to Full Automatic mode\n");
        }
        if ( ent->client->curr_weap == M4_NUM )
        {
                if (!dead)
                        gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/click.wav"), 1, ATTN_NORM, 0);
                ent->client->resp.m4_mode = !(ent->client->resp.m4_mode);
                if ( ent->client->resp.m4_mode )
                        gi.cprintf (ent, PRINT_HIGH, "M4 set to 3 Round Burst mode\n");
                else
                        gi.cprintf (ent, PRINT_HIGH, "M4 set to Full Automatic mode\n");
        }

        if ( ent->client->curr_weap == SNIPER_NUM )
        {
                if (dead)
                        return;
                if ( ent->client->resp.sniper_mode == SNIPER_1X )
                {
                        gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/lensflik.wav"), 1, ATTN_NORM, 0);
                        ent->client->weaponstate = WEAPON_BUSY;
                        ent->client->resp.sniper_mode = SNIPER_2X;
                        ent->client->desired_fov = 45;
                        ent->client->idle_weapon = 6; // 6 frames of idleness
                        ent->client->ps.gunframe = 22;
                }
                else if ( ent->client->resp.sniper_mode == SNIPER_2X )
                {
                        gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/lensflik.wav"), 1, ATTN_NORM, 0);
                        ent->client->resp.sniper_mode = SNIPER_4X;
                        ent->client->desired_fov = 20;
                }
                else if ( ent->client->resp.sniper_mode == SNIPER_4X )
                {
                        gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/lensflik.wav"), 1, ATTN_NORM, 0);
                        ent->client->resp.sniper_mode = SNIPER_6X;
                        ent->client->desired_fov = 10;
                }
                else
                {
                        gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/lensflik.wav"), 1, ATTN_NORM, 0);
                        ent->client->resp.sniper_mode = SNIPER_1X;
                        ent->client->desired_fov = 90;
                        if ( ent->client->pers.weapon )
                                ent->client->ps.gunindex = gi.modelindex( ent->client->pers.weapon->view_model );
                }

        }
        if ( ent->client->curr_weap == KNIFE_NUM )
        {
                if (dead)
                        return;
                if ( ent->client->weaponstate == WEAPON_READY )
                {
                        ent->client->resp.knife_mode = !(ent->client->resp.knife_mode);
                        ent->client->weaponstate = WEAPON_ACTIVATING;
                        if ( ent->client->resp.knife_mode )
                                ent->client->ps.gunframe = 0;
                        else
                                ent->client->ps.gunframe = 106;

                }
        }
        if ( ent->client->curr_weap == GRENADE_NUM )
        {
                if ( ent->client->resp.grenade_mode == 0 )
                {
                        gi.cprintf (ent, PRINT_HIGH, "Prepared to make a medium range throw\n");
                        ent->client->resp.grenade_mode = 1;
                }
                else if ( ent->client->resp.grenade_mode == 1 )
                {
                        gi.cprintf (ent, PRINT_HIGH, "Prepared to make a long range throw\n");
                        ent->client->resp.grenade_mode = 2;
                }
                else
                {
                        gi.cprintf (ent, PRINT_HIGH, "Prepared to make a short range throw\n");
                        ent->client->resp.grenade_mode = 0;
                }

        }

}


// sets variable to toggle nearby door status
void Cmd_OpenDoor_f (edict_t *ent )
{

        ent->client->doortoggle = 1;

        return;
}

void Cmd_Bandage_f ( edict_t *ent )
{
	gitem_t *item;
        if ( (ent->client->weaponstate == WEAPON_READY || ent->client->weaponstate == WEAPON_END_MAG )
                && (ent->client->bleeding != 0 || ent->client->leg_damage != 0 ) 
                && ent->client->bandaging != 1 )
        {       
 
                            // zucc - check if they have a primed grenade

                if ( ent->client->curr_weap == GRENADE_NUM
                        && ( ent->client->ps.gunframe >= GRENADE_IDLE_FIRST
                        && ent->client->ps.gunframe <= GRENADE_IDLE_LAST ) )
                {
                        ent->client->ps.gunframe = 0;
                        fire_grenade2 (ent, ent->s.origin, tv(0,0,0), GRENADE_DAMRAD, 0, 2, GRENADE_DAMRAD*2, false);
			item = FindItem(GRENADE_NAME);
			ent->client->pers.inventory[ITEM_INDEX(item)]--;
			if ( ent->client->pers.inventory[ITEM_INDEX(item)] <= 0 )
			{
				ent->client->newweapon = FindItem( MK23_NAME );

			}
		}		

                ent->client->bandaging = 1;
                ent->client->resp.sniper_mode = SNIPER_1X;
                ent->client->ps.fov = 90;
                ent->client->desired_fov = 90;
                if ( ent->client->pers.weapon )
                        ent->client->ps.gunindex = gi.modelindex( ent->client->pers.weapon->view_model );
        
        }
        else if ( ent->client->bandaging == 1 )
                gi.cprintf (ent, PRINT_HIGH, "Already bandaging\n");         
//FIREBLADE 12/26/98 - fix inappropriate message
        else if (ent->client->bleeding == 0 && ent->client->leg_damage == 0)
                gi.cprintf (ent, PRINT_HIGH, "No need to bandage\n");
        else
                gi.cprintf(ent, PRINT_HIGH, "Can't bandage now\n");
//FIREBLADE
}
// function called in generic_weapon function that does the bandaging

void Bandage( edict_t* ent )
{
        ent->client->leg_noise = 0;
        ent->client->leg_damage = 0;
                ent->client->leghits = 0;
        ent->client->bleeding = 0;
                ent->client->bleed_remain = 0;
//        ent->client->bleedcount = 0;
//        ent->client->bleeddelay = 0;    
        ent->client->bandaging = 0;
        ent->client->leg_dam_count = 0;
        ent->client->attacker = NULL;
                ent->client->bandage_stopped = 1;
                ent->client->idle_weapon = BANDAGE_TIME;
}



void Cmd_ID_f (edict_t *ent )
{

        if (!ent->client->resp.id) {
                gi.cprintf(ent, PRINT_HIGH, "Disabling player identification display.\n");
                ent->client->resp.id = 1;
        } else {
                gi.cprintf(ent, PRINT_HIGH, "Activating player identification display.\n");
                ent->client->resp.id = 0;
        }
        return;
}


static void loc_buildboxpoints(vec3_t p[8], vec3_t org, vec3_t mins, vec3_t maxs)
{
        VectorAdd(org, mins, p[0]);
        VectorCopy(p[0], p[1]);
        p[1][0] -= mins[0];
        VectorCopy(p[0], p[2]);
        p[2][1] -= mins[1];
        VectorCopy(p[0], p[3]);
        p[3][0] -= mins[0];
        p[3][1] -= mins[1];
        VectorAdd(org, maxs, p[4]);
        VectorCopy(p[4], p[5]);
        p[5][0] -= maxs[0];
        VectorCopy(p[0], p[6]);
        p[6][1] -= maxs[1];
        VectorCopy(p[0], p[7]);
        p[7][0] -= maxs[0];
        p[7][1] -= maxs[1];
}

qboolean loc_CanSee (edict_t *targ, edict_t *inflictor)
{
        trace_t trace;
        vec3_t  targpoints[8];
        int i;
        vec3_t viewpoint;

// bmodels need special checking because their origin is 0,0,0
        if (targ->movetype == MOVETYPE_PUSH)
                return false; // bmodels not supported

        loc_buildboxpoints(targpoints, targ->s.origin, targ->mins, targ->maxs);
        
        VectorCopy(inflictor->s.origin, viewpoint);
        viewpoint[2] += inflictor->viewheight;

        for (i = 0; i < 8; i++) {
                trace = do_trace (viewpoint, vec3_origin, vec3_origin, targpoints[i], inflictor, MASK_SOLID);
                if (trace.fraction == 1.0)
                        return true;
        }

        return false;
}


// originally from Zoid's CTF
void SetIDView(edict_t *ent)
{
        vec3_t  forward, dir;
        trace_t tr;
        edict_t *who, *best;
        float   bd = 0, d;
        int i;

        ent->client->ps.stats[STAT_ID_VIEW] = 0;

//FIREBLADE
        if (ent->solid != SOLID_NOT && !teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // won't ever work in non-teams so don't run the code...
        }

        if (ent->client->chase_mode)
        {
                if (ent->client->chase_target &&
                        ent->client->chase_target->inuse)
                {
                        ent->client->ps.stats[STAT_ID_VIEW] = 
                                CS_PLAYERSKINS + 
                                (ent->client->chase_target - g_edicts - 1);
                }               
                return;
        }
//FIREBLADE

        if ( ent->client->resp.id == 1 )
                return;

        AngleVectors(ent->client->v_angle, forward, NULL, NULL);
        VectorScale(forward, 8192, forward);
        VectorAdd(ent->s.origin, forward, forward);
        tr = do_trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID);
        if (tr.fraction < 1 && tr.ent && tr.ent->client) {
                ent->client->ps.stats[STAT_ID_VIEW] = 
                        CS_PLAYERSKINS + (ent - g_edicts - 1);
                return;
        }

        AngleVectors(ent->client->v_angle, forward, NULL, NULL);
        best = NULL;
        for (i = 1; i <= maxclients->value; i++) {
                who = g_edicts + i;
                if (!who->inuse)
                        continue;
                VectorSubtract(who->s.origin, ent->s.origin, dir);
                VectorNormalize(dir);
                d = DotProduct(forward, dir);
                if (d > bd && loc_CanSee(ent, who) && 
//FIREBLADE
                        (who->solid != SOLID_NOT || who->deadflag == DEAD_DEAD) &&
                        (ent->solid == SOLID_NOT || OnSameTeam(ent, who)))
                {
//FIREBLADE
                        bd = d;
                        best = who;
                }
        }
        if (best != NULL && bd > 0.90)
        {
                ent->client->ps.stats[STAT_ID_VIEW] = 
                                CS_PLAYERSKINS + (best - g_edicts - 1);
        }
}


void Cmd_IR_f (edict_t *ent )
{
        int band = 0;
        if ( ir->value )
        {
                if ( ent->client->pers.inventory[ITEM_INDEX(FindItem(BAND_NAME))] )
                {
                        band = 1;
                }
                if ( ent->client->resp.ir == 0 )
                {
                        ent->client->resp.ir = 1;
                        if ( band )
                                gi.cprintf(ent, PRINT_HIGH, "IR vision disabled.\n");
                        else
                                gi.cprintf(ent, PRINT_HIGH, "IR vision will be disabled when you get a bandolier.\n");
                        //      if ( ent->client->ps.rdflags & RDF_IRGOGGLES )
                        //      ent->client->ps.rdflags &= ~RDF_IRGOGGLES;
                }
                else
                {
                        ent->client->resp.ir = 0;
                        if ( band )
                                gi.cprintf(ent, PRINT_HIGH, "IR vision enabled.\n");
                        else
                                gi.cprintf(ent, PRINT_HIGH, "IR vision will be enabled when you get a bandolier.\n");
                        //      if ( !(ent->client->ps.rdflags & RDF_IRGOGGLES) )
                        //      ent->client->ps.rdflags |= RDF_IRGOGGLES;
                }
                
        }
        else
        {
                gi.cprintf(ent, PRINT_HIGH, "IR vision not enabled on this server.\n");
        }
}


// zucc choose command, avoids using the menus in teamplay

void Cmd_Choose_f (edict_t *ent)
{
     
        char             *s;

        s = gi.args();

                // only works in teamplay
                if (!teamplay->value)
                        return;
                
                // convert names a player might try
                if (!stricmp(s, "A 2nd pistol") || !stricmp(s, "railgun"))
                        s = DUAL_NAME;
                if (!stricmp(s, "shotgun"))
                        s = M3_NAME;
                if (!stricmp(s, "machinegun"))
                        s = HC_NAME;
                if (!stricmp(s, "super shotgun"))
                        s = MP5_NAME;
                if (!stricmp(s, "chaingun"))
                        s = SNIPER_NAME;
                if (!stricmp(s, "bfg10k"))
                        s = KNIFE_NAME;
                if (!stricmp(s, "grenade launcher"))
                        s = M4_NAME;

        if ( stricmp(s, MP5_NAME) == 0 )
                        ent->client->resp.weapon = FindItem(MP5_NAME);
                else if ( stricmp(s, M3_NAME) == 0 )
                        ent->client->resp.weapon = FindItem(M3_NAME);
                else if ( stricmp(s, M4_NAME) == 0 )
                        ent->client->resp.weapon = FindItem(M4_NAME);
                else if ( stricmp(s, HC_NAME) == 0 )
                        ent->client->resp.weapon = FindItem(HC_NAME);
                else if ( stricmp(s, SNIPER_NAME) == 0 )
                        ent->client->resp.weapon = FindItem(SNIPER_NAME);
                else if ( stricmp(s, KNIFE_NAME) == 0 )
                        ent->client->resp.weapon = FindItem(KNIFE_NAME);
                else if ( stricmp(s, DUAL_NAME) == 0 )
                        ent->client->resp.weapon = FindItem(DUAL_NAME);
                else if ( stricmp(s, KEV_NAME) == 0 )
                        ent->client->resp.item = FindItem(KEV_NAME);
                else if ( stricmp(s, LASER_NAME) == 0 )
                        ent->client->resp.item = FindItem(LASER_NAME);
                else if ( stricmp(s, SLIP_NAME) == 0 )
                        ent->client->resp.item = FindItem(SLIP_NAME);
                else if ( stricmp(s, SIL_NAME) == 0 )
                        ent->client->resp.item = FindItem(SIL_NAME);
                else if ( stricmp(s, BAND_NAME) == 0 )
                        ent->client->resp.item = FindItem(BAND_NAME);
                else
                {
                        gi.cprintf(ent, PRINT_HIGH, "Invalid weapon or item choice.\n");
                        return;
                }
                gi.cprintf(ent, PRINT_HIGH, "Weapon selected: %s\nItem selected: %s\n", (ent->client->resp.weapon)->pickup_name, (ent->client->resp.item)->pickup_name);

}
                
