D:\cygwin\wf_fusion_src_r8\WeaponsFactory\wf_decoy.c

/*
==============================================================================
The Weapons Factory - 
Decoy Mod

Original code by Gregg Reno / Various (see credits)

Modified code by Keith Pase

Copyright (C) 1997-2003 Weapons Factory Software

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  wf_decoy.c -- Mini bot decoy.
  
==============================================================================
*/

#include "..\game\g_local.h"
#include "..\game\m_player.h"
#include "..\game\gs_pmodels.h"

qboolean IsCyborg (edict_t *ent);

static int  sound_idle;
static int  sound_sight1;
static int  sound_sight2;
static int  sound_pain;
static int  sound_death;
static int  sound_on;

edict_t *SV_TestEntityPosition (edict_t *ent);

/*
=============
DecoyFit - see if the decoy will fit where the player is
creating it.
 
   -taken from m_move.c, M_CheckBottom function
=============
*/
qboolean DecoyFit (edict_t *ent, edict_t *owner)
{
//  vec3_t  mins, maxs, start, stop;
    vec3_t  mins, maxs;
//  trace_t trace;
    
    VectorAdd (ent->s.origin, ent->r.mins, mins);
    VectorAdd (ent->s.origin, ent->r.maxs, maxs);

//See if the mins,maxs or origin are in something solid


// These are returning whacked results near patches/meshes.

/*
    if (gi.pointcontents (mins) == CONTENTS_SOLID)
    {
        safe_cprintf (owner, PRINT_HIGH, "Decoy Fail Check 1.\n");
        return qfalse;
    }
    if (gi.pointcontents (maxs) == CONTENTS_SOLID)
    {
        safe_cprintf (owner, PRINT_HIGH, "Decoy Fail Check 2.\n");
        return qfalse;
    }
*/
    if (trap_PointContents (ent->s.origin) == CONTENTS_SOLID)
    {
        return qfalse;
    }
/*
    //Trace line from these two points to see if anything is in-between
    trace = gi.trace(mins, ent->s.origin, ent->s.origin, maxs, ent, MASK_SHOT );
    if (trace.fraction != 1.0)  // 1.0 = nothing in between
    {
        safe_cprintf (owner, PRINT_HIGH, "Decoy Fail Check 4.\n");
        return qfalse;
    }

    //Trace line from decoy origin to owner origin to see if anything is in-between

    trace = gi.trace(ent->s.origin, ent->s.origin, ent->s.origin, owner->s.origin, ent, MASK_SHOT );
    if (trace.fraction != 1.0)  // 1.0 = nothing in between
    {
        safe_cprintf (owner, PRINT_HIGH, "Decoy Fail Check 5.\n");
        return qfalse;
    }
*/
    if (SV_TestEntityPosition (ent))
    {
        return qfalse;
    }

    return qtrue;
}

#define DECOY_FIRST_FRAME   0
#define DECOY_LAST_FRAME    39

void FaceTarget (edict_t *self)
{
    vec3_t v;
    
    VectorSubtract (self->enemy->s.origin, self->s.origin, v);
    self->ideal_yaw = vectoyaw(v);
    self->yaw_speed = 60;
    M_ChangeYaw(self);
    G_Printf ("FaceTarget\n");
}

qboolean isFlagHome (edict_t *self)
{
    edict_t *target;
    char *str;

    if (self->wf_team == 1)
        str = "team_CTF_blueflag";
    else
        str = "team_CTF_redflag";
    
    target = G_Find(NULL, FOFS(classname), str);

    if ((!(!target)) && (target->r.solid == SOLID_NOT))
        return qtrue;
    else
        return qfalse;
}
void FindEnemyFlagCarrier (edict_t *self)
{
    int i;
    extern gitem_t *flag1_item;
    extern gitem_t *flag2_item;
    
    for (i = 1; i <= maxclients->value; i++)
    {
        if (self->wf_team == 1)
        {
            if (game.edicts[i].r.inuse &&
                game.edicts[i].r.client->pers.inventory[ITEM_INDEX(flag2_item)]) 
            {
                self->enemy = &game.edicts[i];
            }
        }
        else  //if (ent->wf_team == 2)
        {          
            if (game.edicts[i].r.inuse &&
                game.edicts[i].r.client->pers.inventory[ITEM_INDEX(flag1_item)]) 
            {
                // enemy has it
                self->enemy = &game.edicts[i];
                
            }
        }
    }
}

void DecoyFindTarget (edict_t *self)
{
    edict_t *blip;
    vec3_t v;
    trace_t     tr;
    float dist;
    int     range;
    qboolean carriervisible;

    carriervisible = qfalse;

    blip = NULL;

    range = 900;
    
    if (!isFlagHome)
    {
        FindEnemyFlagCarrier (self);

        trap_Trace (&tr, self->s.origin, NULL, NULL, self->enemy->s.origin, self, MASK_SOLID);

        if (tr.fraction != 1.0)
            carriervisible = qfalse;

        VectorSubtract (self->enemy->s.origin, self->s.origin, v);
        
        dist = VectorLength(v);

        if (visible(self, self->enemy) && dist > 700)
            carriervisible = qtrue;
    }
    if (!carriervisible)
    {
        while (blip = findradius (blip, self->s.origin, range))
        {

            if (!blip->r.inuse)
                continue;
            if (strcmp(blip->classname, "hook") == 0 )
                continue;
            if (blip->r.solid == SOLID_NOT)
                continue;
            if (blip->health <= 0)
                continue;
            if (strcmp(blip->classname, "decoy") == 0)
                continue;

            trap_Trace (&tr, self->s.origin, NULL, NULL, blip->s.origin, self, MASK_SOLID);

            if (tr.fraction != 1.0)
                continue;

            VectorSubtract (blip->s.origin, self->s.origin, v);
            
            dist = VectorLength(v);

            if (!visible(self, blip) && dist > 700)
                continue;

            self->enemy = blip;
        }
    }
}

void DecoyLookAround (edict_t *self)
{
    if(self->sentrydelay<level.time)
    {
        if(self->PlasmaDelay)
            self->PlasmaDelay=0;
        else
            self->PlasmaDelay=1;
        
        self->sentrydelay=level.time + 1;
    }
    if(self->PlasmaDelay)
        self->s.angles[YAW]+= ( ( 90 * random() ) + 4 );
    else
        self->s.angles[YAW]-= ( ( 90 * random() ) + 4 );
    if(self->s.angles[YAW]<0)
        self->s.angles[YAW]+=360;
    else if(self->s.angles[YAW]>360)
        self->s.angles[YAW]-=360;

}


void Decoy_Think(edict_t *self)
{
    int contents;
    float choose;
    float targetmaxvelocity;


    contents = (int)trap_PointContents(self->s.origin);
    if (contents & CONTENTS_NODROP)
    {
        // regular death
        self->deadflag = DEAD_DEAD;
        self->takedamage = DAMAGE_YES;

        G_Sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM);
        G_PrintMsg (self->creator, PRINT_HIGH, "Decoy Removed.\n");

        //do a BFG kind of explosion where the decoy was
        if (self->wf_team == CTF_TEAM1) //team 1 is red
            G_SpawnEvent ( EV_BFG_EXPLOSION, 0, self->s.origin );
        else
            G_SpawnEvent ( EV_BFG_EXPLOSION, 0, self->s.origin );

        //Clear pointer of owner
        self->creator->decoy = NULL;

        //Remove entity instead of playing death sequence
        G_FreeEdict (self);
            return;
    }

    if (self->s.frame < DECOY_FIRST_FRAME)
        self->s.frame = DECOY_FIRST_FRAME;
    else if (self->s.frame > DECOY_LAST_FRAME)
        self->s.frame = DECOY_FIRST_FRAME;

    DecoyFindTarget (self);

    if (!self->enemy)
    {
        self->nextthink = level.time + 5;
        G_Printf ("No Target\n");
    }
    else
    {
        choose = random();
        targetmaxvelocity = max( abs (self->enemy->velocity[0]), abs (self->enemy->velocity[1]) );
        if (targetmaxvelocity <= 60)
        {
            self->enemy->bordometime++;
            if (self->enemy->bordometime >= 60)
            {
                if (choose < 0.7)
                {
                    DecoyLookAround (self);
                    G_Printf ("DecoyLookAround\n");
                    self->sentrydelay=level.time + 3;
                }
                if (self->enemy->bordometime >=65)
                {
                    self->enemy->bordometime = 1;
                    FaceTarget (self);
                    FaceTarget (self);
                    FaceTarget (self);
                    FaceTarget (self);
                }
                self->nextthink = level.time + 3;
            }
            else
                self->nextthink = level.time + .1;
        }
        else
        {       
            FaceTarget (self);
            self->nextthink = level.time + .1;
            self->enemy->bordometime = 1;
        }

    }
}



// STAND frames
void decoy_idle (edict_t *self)
{
    if (random() > 0.8)
        G_Sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE);
}

void decoy_sight(edict_t *self, edict_t *other)
{
if (random() < 0.5)
    G_Sound (self, CHAN_VOICE, sound_sight1, 1, ATTN_NORM);
else
    G_Sound (self, CHAN_VOICE, sound_sight2, 1, ATTN_NORM);
}

//
// DEATH sequence
//
void decoy_pain (edict_t *self, edict_t *other, float kick, int damage)
{
    if (level.time < self->pain_debounce_time)
        return;

    self->pain_debounce_time = level.time + 3;
    G_Sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM);
}


void decoy_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
    //  int     n;

    if (self->deadflag == DEAD_DEAD)
        return;

    // regular death
    self->deadflag = DEAD_DEAD;
    self->takedamage = DAMAGE_YES;

    G_Sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM);

    //do a BFG kind of explosion where the decoy was
    if (self->wf_team == CTF_TEAM1) //team 1 is red
    {
        G_SpawnEvent ( EV_BFG_EXPLOSION, 0, self->s.origin );
    }
    else
    {
        G_SpawnEvent ( EV_BFG_EXPLOSION, 0, self->s.origin );
    }
    
    G_PrintMsg (self->creator, PRINT_HIGH, "Decoy Destroyed!\n");

    //Clear pointer of owner
    self->creator->decoy = NULL;

    //Remove entity instead of playing death sequence
    G_FreeEdict (self);

}


//
// SPAWN
//
qboolean spawn_decoy (edict_t *owner)
{
    edict_t *self;
    vec3_t forward;

    self = G_Spawn();

    // Place decoy 100 units forward of our position
    AngleVectors(owner->r.client->v_angle, forward, NULL, NULL);
    VectorMA(owner->s.origin, 100, forward, self->s.origin);

    //set the team
    self->wf_team = owner->r.client->resp.ctf_team;

    self->s.skinnum = owner->s.skinnum;
    self->s.number  = owner->s.number;

    self->s.modelindex  =   255;
    self->s.modelindex2 =   255;

    self->think = Decoy_Think;
    self->nextthink = level.time + .1;

    //Set the skin color

    self->classname = "decoy";
    
//  self->monsterinfo.scale = MODEL_SCALE;
    VectorSet (self->r.mins, -16, -16, -24);
    VectorSet (self->r.maxs, 14, 14, 28); // 16,16,32
    self->movetype = MOVETYPE_STEP;
    self->r.solid = SOLID_BBOX;
    self->r.clipmask = MASK_PLAYERSOLID;
    self->takedamage = DAMAGE_YES;

    self->s.effects |= EF_DECOY;
    self->s.type |= ET_PLAYER;

    self->sentrydelay= level.time + 5;

    self->s.renderfx = RF_FRAMELERP;

    self->mass = 100;
    self->pain = decoy_pain;
    self->die = decoy_die;

//newdecoy for proxies etc if think ever gets fixed remove this
    self->max_health = self->health;

    //Set up sounds
    sound_idle      =   trap_SoundIndex  ("sound/decoy/idle.wav.wav");
    sound_sight1    =   trap_SoundIndex  ("sound/decoy/intruder.wav");
    sound_sight2    =   trap_SoundIndex  ("sound/decoy/intruder.wav");
    sound_pain      =   trap_SoundIndex  ("sound/decoy/decoy_pain.wav");

    //Set up on/off sounds
//  if (IsFemale (owner))
//  {
        sound_death = trap_SoundIndex ("sound/decoy/fdecoyof.wav");
        sound_on = trap_SoundIndex ("sound/decoy/fdecoyon.wav");
/*
    }
        else if (IsCyborg (owner))
    {
            sound_death = gi.soundindex  ("sound/decoy/cdecoyof.wav");
            sound_on = gi.soundindex  ("sound/decoy/cdecoyon.wav");
    }
    else
    {
        sound_death = gi.soundindex  ("sound/decoy/mdecoyof.wav");
        sound_on = gi.soundindex  ("sound/decoy/mdecoyon.wav");
    }
*/

    self->health = 30;
    self->max_health = 30;
    self->gib_health = -30;


    // Face the decoy the same direction as player
    self->s.angles[PITCH] = owner->s.angles[PITCH];
    self->s.angles[YAW] = owner->s.angles[YAW];
    self->s.angles[ROLL] = owner->s.angles[ROLL];

    // See if the decoy will fit
    if (DecoyFit(self, owner) == qfalse)
    {
        G_FreeEdict(self);
        G_PrintMsg (owner, PRINT_HIGH, "Decoy won't fit.  Try aiming a little differently.\n");
        return qfalse;
    }
 
    //Link two entities together
    owner->decoy = self;    //for the owner, this is a pointer to the decoy
    self->creator = owner;  //for the decoy, this is a pointer to the owner

//  self->r.owner = owner;

    FUSION_MAPPING_DecoyPlaced(self);

    trap_LinkEntity (self);

    //Play the startup sound
    G_Sound (self, CHAN_VOICE, sound_on, 1, ATTN_NORM);

    //Reduce cell count
    if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
        owner->r.client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= DECOY_CELLS;

    return qtrue;
}


// SP_Decoy - Handle DECOY command
void SP_Decoy(edict_t *self) {

    //See if we should decoy turn it on or off
    char    *string;
    int  turnon;

    if ((int)wfflags->value & WF_NO_DECOYS)
    {
        G_PrintMsg (self, PRINT_HIGH, "Sorry, Decoys are DISABLED on this server.\n");
        return;
    }

    string = trap_Cmd_Args ();

    if (Q_stricmp ( string, "on") == 0) 
        turnon = qtrue;
    else if (Q_stricmp ( string, "off") == 0) 
        turnon = qfalse;
    else {  //toggle status
        if (self->decoy) turnon = qfalse;
        else turnon = qtrue;
    }

    //If they want to turn it on and it's already on, return
    if ( (turnon == qtrue) && (self->decoy) ) return;

    //If they want to turn it off and it's already off, return
    if ( (turnon == qfalse) && !(self->decoy) ) return;

    //Remove decoy if it exists
    if ( self->decoy ) {
        G_Sound (self, CHAN_VOICE, sound_death, .5, ATTN_NORM);
        G_FreeEdict(self->decoy);
        self->decoy = NULL;
        G_PrintMsg (self, PRINT_HIGH, "Decoy destroyed.\n");
        return;
        }

    //Cant create decoy in observer/spectator mode
    if (self->r.solid == SOLID_NOT)
    {
        G_PrintMsg (self, PRINT_HIGH, "Can't create decoy in spectator mode (nice try!).\n");
        return;
    }

    //Create decoy if you have enough cells
    if (self->r.client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < DECOY_CELLS)
    {
        G_PrintMsg (self, PRINT_HIGH, "Not enough cells for decoy.\n");
        return;
    }
    if (spawn_decoy(self))
    {
        G_PrintMsg (self, PRINT_HIGH, "Decoy created.\n");
    }
}