http://www.codeguru.com/devstudio_macros/HtmlOut.shtml (for jalisko)

 

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

Original code by Gregg Reno

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.

  Modified code by Keith Pase

==============================================================================
*/

#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
/*
    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)
    {
        G_PrintMsg  (owner, PRINT_HIGH, "DEBUG: Decoy Fail Check CONTENTS_SOLID.\n");
        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_MONSTERSOLID );
    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_MONSTERSOLID );
    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))
    {
        G_PrintMsg (owner, PRINT_HIGH, "DEBUG: Decoy Fail Check SV_TestEntityPosition.\n");
        return qfalse;
    }

    return qtrue;
}

#define DECOY_FIRST_FRAME   0
#define DECOY_LAST_FRAME    39

void Decoy_Think(edict_t *self)
{
    int contents;
    //  ++self->s.frame;

    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;

    self->nextthink = level.time + 0.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_fire (edict_t *self, int flash_number)
    {
    vec3_t  start;
    vec3_t  forward, right, up;
    vec3_t  aim;
    vec3_t  dir;
    vec3_t  end;
    float   r, u;
    int     flash_index;

//  flash_index = shotgun_flash[flash_number];

    AngleVectors (self->s.angles, forward, right, NULL);
    G_ProjectSource (self->s.origin, monster_flash_offset[flash_index], forward, right, start);

    if (flash_number == 5 || flash_number == 6)
    {
        VectorCopy (forward, aim);
    }
    else
        {
        VectorCopy (self->enemy->s.origin, end);
        end[2] += self->enemy->viewheight;
        VectorSubtract (end, start, aim);
        VecToAngles (aim, dir);
        AngleVectors (dir, forward, right, up);

        r = crandom()*1000;
        u = crandom()*500;
        VectorMA (start, 8192, forward, end);
        VectorMA (end, r, right, end);
        VectorMA (end, u, up, end);

        VectorSubtract (end, start, aim);
        VectorNormalize (aim);
        }

    monster_fire_shotgun (self, start, aim, 2, 1, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_index, MOD_SHOTGUN);
    }

// Fire weapon
void decoy_fire1 (edict_t *self)
{
        decoy_fire (self, 0);
}

mframe_t decoy_frames_attack1 [] =
{
    ai_charge, 0,  NULL,
    ai_charge, 0,  NULL,
    ai_charge, 0,  decoy_fire1,
    ai_charge, 0,  NULL,
    ai_charge, 0,  NULL,
    ai_charge, 0,  NULL,
    ai_charge, 0,  NULL,
    ai_charge, 0,  NULL
};
mmove_t decoy_move_attack1 = {TORSO_DROPHOLD, TORSO_DROPHOLD_time, decoy_frames_attack1, decoy_run};

void decoy_attack(edict_t *self)
    {
    self->monsterinfo.currentmove = &decoy_move_attack1;
    }
*/
//
// SIGHT frames
//

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 + 5;

    //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_AIM;

//  self->s.effects |= EF_FLIES|RF_SHELL_RED;

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

//newdecoy for proxies etc if think ever gets fixed remove this
    self->r.svflags |= SVF_MONSTER;
    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

    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;

    G_PrintMsg (self, PRINT_HIGH, "DEBUG: DECOY command intercept.\n");

    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");
    }